又名:包含,组合,层次,复合关系
是一个类,由其他类对象共同组成
#include using namespace std;class Desk{int a;
public:Desk(){cout << "Desk构造" << this << endl;}~Desk(){cout << "Desk析构" << endl;}
};class Person{int a;
public:Person(){cout << "Person构造" << this << endl;}~Person(){cout << "Person析构" << endl;}void say(){cout << "hello" << endl;}
};class Chair{int a;
public:Chair(){cout << "Chair构造" << this << endl;}~Chair(){cout << "Chair析构" << endl;}
};class Room{
private:Person p;Desk d;Chair c;
public:Room(){cout << "Room构造" << this << endl;}~Room(){cout << "Room析构" << endl;}
};int main()
{Room r;Room *r1=new Room();((Person *)(r1))->say();delete r1;return 0;
}
has_a关系中构造与析构的先后顺序:
内存关系布局图:
类与函数,类与类之间的亲密关系。
存在两种形式:友元函数、友元类
友元函数:
语法:friend 函数声明;
在类中使用friend 关键字声明类外的某个函数为本类的友元函数,类内即生效
特性:
#include using namespace std;class Boy{int money;
public:Boy(int money){this->money=money;}friend void beautifulGril(Boy &boy);//friend关键字,声明成为友元函数};void beautifulGril(Boy &boy){cout << boy.money << endl;
}int main()
{Boy boy(10000);beautifulGril(boy);return 0;
}
友元类:
语法:friend class 友元类名
单文件,只能实现单友谊
#include using namespace std;class Boy{
private:string name;int age;
public:Boy(string name,int age){this->name=name;this->age=age;}void hard_work(){cout << "woking" << endl;}friend class Gril;
};class Gril{
private:string name;int age;
public:Gril(string name,int age){this->name=name;this->age=age;}void go_shopping(){cout << "shopping" << endl;}void go_shopping(Boy& boy){cout << boy.name << " .ing " << this->name << " shopping " << endl;}};int main()
{Boy boy("yao",18);Gril gril("sun",17);gril.go_shopping(boy);return 0;
}
分文件编程
boy.h:
#ifndef BOY_H
#define BOY_H
#include
using namespace std;
#include "girl.h"
class Girl;class Boy
{
public:string name;int age;
public:Boy(string name,int age);void hard_work(Girl& girl);friend class Girl;
};#endif // BOY_H
girl.h:
#ifndef GRIL_H
#define GRIL_H
#include
using namespace std;
#include "boy.h"
class Boy;class Girl
{
public:string name;int age;
public:Girl(string name,int age);void goshopping(Boy& boy);friend class Boy;
};#endif // GRIL_H
boy.c:
#include "boy.h"
#include "girl.h"Boy::Boy(string name,int age)
{this->name=name;this->age=age;
}void Boy::hard_work(Girl& girl){cout << girl.name << " accompany.ing " << this->name << " Set out for Happiness" << endl;
}
girl.c:
#include "girl.h"Girl::Girl(string name,int age)
{this->name=name;this->age=age;
}
void Girl::goshopping(Boy &boy){cout << boy.name << " be being " << this->name << " shopping" << endl;
}
main.c:
#include
#include "girl.h"
#include "boy.h"using namespace std;int main()
{Boy boy("liangliang",18);Girl girl("minmin",17);girl.goshopping(boy);boy.hard_work(girl);return 0;
}
不存在传导关系(即A与B是好友,B与C是好友,A和C不一定是好友)
继承(派生)作用:提高代码复用性和高拓展性
语法:class+子类 :继承权限+父类
#include using namespace std;class Car{int weight;
public:void run(){cout << "runing" << endl;}
};class BMW : public Car//is a Car 类
{
public:int size;void show(){cout << "i am is BMW" << endl;}
};class BNZ : private Car//is a Car 类
{
public:int size;void show(){this->run();cout << "i am is BNZ" << endl;}
};class TRAM : public BMW//is a BMW 类
{
public:void say(){cout << "my father is BMW" << endl;}
};int main()
{cout << sizeof (Car) << endl;Car c;c.run();cout << sizeof (BMW) << endl;BMW b;b.run();b.show();TRAM tram;cout << sizeof (TRAM) << endl;tram.run();tram.show();tram.say();BNZ bnz;bnz.show();return 0;
}
理解:抽象定义基类(父类),继承(派生)子类(继承类)。基类本身功能不可修改,此原则叫做开闭原则,即拓展开放,对修改关闭。
继承关系中构造与析构的先后顺序:
单继承关系下内存布局:
#include using namespace std;class A{int a;
public:A(){cout << "i am is A structure" << this << endl;}~A(){cout << "i am is A destruct" << endl;}
};class B : public A{int b;
public:B(){cout << "i am is B structure" << this << endl;}~B(){cout << "i am is B destruct" << endl;}
};int main()
{A a;B b;return 0;
}
注意:派生中的基类,不同于本身类
解决同名问题(隐藏机制):
当继承类与基类存在同名,基类隐藏在类域之中,如果想访问基类,方法:域名::访问
#include using namespace std;class A{
public:int a=100;A(){cout << "i am is A structure" << this << endl;}~A(){cout << "i am is A destruct" << endl;}void show(){cout << "A inside" << endl;}
};class B : public A{
public:int a=1000;B(){cout << "i am is B structure" << this << endl;}~B(){cout << "i am is B destruct" << endl;}void show(){cout << "B inside" << endl;}
};int main()
{B b;cout << b.a << endl;//1000cout << b.A::a << endl;//100b.show();//B insideb.A::show();//A insidereturn 0;
}
类型兼容规则:is_a关系:子类是基类的一个拓展。
兼容语法如下:
- 基类指针可以安全的访问继承类对象
- 当父类不存在默认构造时,应在继承类的构造函数的初始化列表中初始化
- 父类指针只能调用子类类域中的所包含的基类中的类域方法和属性,但存在继承类不能析构问题。
- 所以基类指针释放资源时,要强转成子类指针才能释放,避免内存泄漏。
#include using namespace std;
class Person{
private:string name;int age;
public:Person(string name,int age){this->name=name;this->age=age;cout << "Person structure" << endl;}void showInfo(){cout << "姓名:" << this->name << ",年龄" << this->age << endl;}int getAge(){return this->age;}string getName(){return this->name;}~Person(){cout << "Pseron desturct" << endl;}
};class Stu:public Person
{
private:int score;int *p;
public://当父类中没有默认构造,应该在子类的构造函数的初始化列中指定。Stu(string name,int age,int score):Person(name,age){this->score=score;this->p=new int[1024];cout << "stu structure" << endl;}void showInfo(){cout << "姓名:" << this->getName() << ",年龄" << this->getAge() << ",成绩" << score <showInfo();
// Stu* stu=new Person("yao",19);//不能子类指向父类//查看基类指针是否指向继承类(继承类是否开辟空间),方法:把基类指针强转成继承类指针((Stu*)p)->showInfo();//强转存在风险,原因:继承类可能不开辟空间cout << "强制转换的风险性" << endl;Person* q=new Person("minmin",18);q->showInfo();((Stu*)q)->showInfo();delete (Stu*)p;delete q;return 0;
}
语法:class+子类:继承权限1+父类1,继承权限2+父类2,继承权限n+父类n
优点:可以大大提高代码的复用性
缺点:
推荐:使用多继承多个抽象类,因为抽象类没有具体的属性,只有**纯虚函数**
多继承的内存布局:
父类指针指向子类对象时:
#include using namespace std;class Phone{
public:string logo;int power;
public:Phone(){cout << "Phone structure" << this << endl;}~Phone(){cout << "Phone destruct" << endl;}void display(){cout << "手机正在显示中" << endl;}
};class Computer{
public:string logo;int power;
public:Computer(){cout << "Computer structrue" << this << endl;}~Computer(){cout << "Computer deatruct" << endl;}void display(){cout << "电脑正在显示中" << endl;}
};class Pad:public Phone,public Computer
{
public:Pad(){cout <<"Pad structrue"<< this << endl;}~Pad(){cout <<"Pad destruct"<< endl;}void playgame(){cout << this->Phone::logo << endl;this->Phone::display();cout << this->Phone::power << endl;}
};int main()
{cout << sizeof(Pad) << endl;Pad p;p.playgame();Phone* phone=new Pad ();cout << phone << endl;delete (Pad*)phone;Computer* computer=new Pad ();cout << computer << endl;delete (Pad*)computer;return 0;
}
分析图:
常看到的模型:
问题解决(virtual关键字):虚继承
#include using namespace std;class A{
public:int a;string name;A(){cout << "i am is A structure" << this << endl;}~A(){cout << "i am is A destruct" << endl;}
};class B :virtual public A{
public:int b;B(){cout << "i am is B structure" << this << endl;}~B(){cout << "i am is B destruct" << endl;}
};class C :virtual public A{
public:int c;C(){cout << "i am is C structure" << this << endl;}~C(){cout << "i am is C destruct" << endl;}
};class D : public B,public C{
public:int d;D(){cout << "i am is D structure" << this << endl;}~D(){cout << "i am is D destruct" << endl;}
};int main()
{D d;cout << sizeof (d) << endl;//没有加virtual 96 个//加后72个,极大的节省了空间//原因,virtual会安插虚基表指针,生成两个指针//A为40个字节,B中有一个指针一个int,对齐后16个字节,C中有一个指针一个int,对齐后16个字节,D中int进行对齐,所以共72字节return 0;
}
虚基表指针指向虚基表
虚基表保存着虚基表偏移量
通过偏移量找到父类中的属性
开发经验:
has_a>单继承>多继承(多个抽象类)>菱形继承(virtual)
动态多态、绑定多态
形成条件(必须满足三条):
类中成员函数前加virtual,特殊的构造函数不能加vritual
虚函数特性:类中的虚函数具有虚属性,也就是说函数逻辑可以在子类之中完成重写
重写:即是类似于子类之中对父类中的同名函数的重新定义
两个关键字:override(检查重写)与final(结束重写)
#include using namespace std;class Person{
private:string name;int age;
public:Person(string name,int age){this->name=name;this->age=age;}virtual void showInfo(){cout << "姓名:" << this->name << ",年龄:" << this->age << endl;}int getAge(){return this->age;}string getName(){return this->name;}
};class Stu:public Person{
private:int score;
public:Stu(string name,int age,int score):Person(name,age){this->score=score;}//c++11提供的信关键字override重写,用来检查重写父类虚函数的返回值与形参类型是否匹配,不匹配error//关键字final进行重写结束(替换在override位置上)void showInfo()override{//同名就是虚函数cout << "姓名:" << this->getName() <<"年龄:" << this->getAge() << "成绩:" << score << endl ;}
};int main()
{Person* person=new Stu("yao",19,100);person->showInfo();//现在是子类的,因为加virtual,进行多态重写Person* p=new Stu ("minmin",18,90);p->showInfo();((Stu*)person)->showInfo();Stu stu("sunsun",18,200);stu.Person::showInfo();stu.showInfo();return 0;
}
关系解析图:
虚函数表指针由构造函数创建 .rodata段存放虚函数表 虚函数表存放虚函数的地址偏移量
**通过virtual关键字进行重写 **override关键字进行检查重写 final关键字进行结束重写
实现飞行员可以驾驶各类飞机:
思路:
#include using namespace std;
class Plan{
public:virtual void fly(){cout << "飞机正在飞" << endl;}
};class AirBus:public Plan{
public:void fly()override{cout << "客中客车在飞行" << endl;}
};class J20:public Plan{
public:void fly()override{cout << "J20在飞行" << endl;}
};class Polit{
public:void driver(Plan& plan){//Plan& plan=plan.fly();}
};int main()
{AirBus airbus;J20 j20;Polit polit;polit.driver(airbus);polit.driver(j20);return 0;
}
问题:子析构无法得到执行
解决方案:虚析构(基类析构函数为虚析构)
好处:避免内存泄漏,无需强转
原理:多态状态下,子析构进行重写
#include using namespace std;class Person{
private:string name;int age;
public:Person(string name,int age){this->name=name;this->age=age;cout << "Person structure" << endl;}virtual void showInfo(){cout << "姓名:" << this->name << ",年龄:" << this->age << endl;}int getAge(){return this->age;}string getName(){return this->name;}virtual ~Person(){ //设计为虚析构cout << "Person destruct" << endl;}
};class Stu:public Person{
private:int score;
public:Stu(string name,int age,int score):Person(name,age){this->score=score;cout << "Stu structure" << endl;}//c++11提供的信关键字override重写,用来检查重写父类虚函数的返回值与形参类型是否匹配,不匹配error//关键字final进行重写结束(替换在override位置上)void showInfo()override{//同名就是虚函数cout << "姓名:" << this->getName() <<"年龄:" << this->getAge() << "成绩:" << score << endl ;}~Stu()override{cout << "Stu destruct" << endl;}
};int main()
{Person* person=new Stu("yao",19,100);person->showInfo();//现在是子类的,因为加virtual,进行多态重写delete (Stu*)person;cout << "-------------------------------------------------" << endl;Person* p=new Stu ("minmin",18,90);p->showInfo();delete p; //不强转,子析构不会存在,解决方案:虚析构cout << "-------------------------------------------------" << endl;Person* q=new Person("sun",18);q->showInfo();delete (Stu*)q;//存在问题,如果不是多态,会报错cout << "-------------------------------------------------" << endl;((Stu*)person)->showInfo();Stu stu("sunsun",18,200);stu.Person::showInfo();stu.showInfo();return 0;
}
介绍:无函数体,无任何逻辑,是特殊的虚函数,是作为函数API使用的
逻辑:在子类中重写
分析:用名字实现功能,本身不定义,由子类继承后进行重写定义,别名《接口函数》
应用:实现多态,顶层类的设计使用
规则:该抽象类是不可以定义对象的,只能使用指针或引用,指向或引用子类重写纯虚函数的子类对象。
包含纯虚函数的类,也叫做抽象类,也被称接口类
如果子类没有重写纯虚函数,那么子类也将成为抽象类
#include using namespace std;class A{
public:virtual void showInfo()=0;A(){cout << "A structure" << endl;}~A(){cout << "A destruct" << endl;}
};class B:public A{
public:void showInfo() override{cout << "hello world" << endl;}B(){cout << "B structure" << endl;}~B(){cout << "B destruct" << endl;}
};class C:public A{
public:void showInfo() override{cout << "hi world" << endl;}C(){cout << "C structure" << endl;}~C(){cout << "C destruct" << endl;}
};class D{
public:virtual void showInfo_D()=0;D(){cout << "D structure" << endl;}~D(){cout << "D destruct" << endl;}
};class E:public A,public D{
public:void showInfo() override{cout << "hello world" << endl;}void showInfo_D() override{cout << "hi world" << endl;}E(){cout << "E structure" << endl;}~E(){cout << "E destruct" << endl;}
};int main()
{A *a=new B ();a->showInfo();delete (B*)a;
// A b;//error,不能定义接口类,只能定义指针或引用,指向或引用子类
// A* b=new A ();//error,不能定义接口类,只能定义指针或引用,指向或引用子类A* c=new C();c->showInfo();delete (C*)c;cout << "-------------------解决多继承问题-------------------" << endl;A* e=new E ();e->showInfo();delete (E*)e;D* f=new E ();f->showInfo_D();delete (E*)f;E h;h.showInfo();h.showInfo_D();return 0;
}
强转,临时有效
可转基本类型:
#include using namespace std;int main()
{int a=500;double b=3.14;cout << (int)b << endl;cout << static_cast(b) << endl;double* p=&b;cout << *p << endl;
// cout << *(int *)p << endl;//只取小端后4个字节
// cout << *(static_cast(p)) << endl;//static_cast强转基本类型指针不被允许,这是一种安全保护return 0;
}
不同基本类型之间避免转换
可转自定义类型:
#include using namespace std;class A{
private:string my_Astr="la la la la" ;
public:void showInfo_A(){cout << this->my_Astr << endl;}
};class B: public A{
private:string my_Bstr="ha ha ha ha" ;
public:void showInfo_B(){cout << this->my_Bstr << endl;}
};int main()
{A* aa=new B ();((B*)aa)->showInfo_B();static_cast(aa)->showInfo_B();A* a=new A();//存在的问题
// ((B*)a)->showInfo_B();
// static_cast(a)->showInfo_B();// 出现bugreturn 0;
}
在继承类中,向下转型并不安全,向上是安全的,原因:可能不会开辟空间,安全由程序员来保证
依赖多态完成,会去查看虚函数表中的运行时类型信息识别槽
如果不存在,返回nullptr,直接杀死进程,并报错,安全
#include using namespace std;class A{
private:string my_Astr="la la la la" ;
public:void showInfo_A(){cout << this->my_Astr << endl;}virtual ~A()=default; //使用c++11的默认函数体
};class B: public A{
private:string my_Bstr="ha ha ha ha" ;
public:void showInfo_B(){cout << this->my_Bstr << endl;}
};int main()
{A* aa=new B ();((B*)aa)->showInfo_B();static_cast(aa)->showInfo_B();dynamic_cast(aa)->showInfo_B();A* a=new A();
// ((B*)a)->showInfo_B();
// static_cast(a)->showInfo_B();// 出现bug
// dynamic_cast(a)->showInfo_B();//直接杀死进程return 0;
}
使用场景:改变指针或者引用的常属性
#include using namespace std;int main()
{int a=100;const int *p=&a;*((int*)p)=500;cout << a << endl;*const_cast(p)=500;cout << a << endl;cout << *p << endl;cout << "-----------------改变常引用类型----------" << endl;const int& b=a;const_cast(b)=600;cout << b << endl;cout << a << endl;int c=10;int* const q=&a;//const_cast(q)=&c;//const不能改变指针的指向,原因:const_cast的设计是根据c++引用而来的,引用不能只能初始化一次return 0;
}
注意:
const不能改变指针的指向,原因:const_cast的设计是根据c++引用而来的,引用只能初始化一次
应用场景:任意类型间的转换
范围:广泛
难度:困难
分析:特别底层,没有类型检查和格式转换,仅仅是简单的二进制转换,不安全
#include using namespace std;int main()
{int a=100;int *p=&a;cout << reinterpret_cast(p) << endl;return 0;
}
作用:用引用引用右值引用
底层实现:引用右值在栈上的临时变量,是临时变量的别名
规则:只能进行右值引用,不能引用左值(类型不匹配,相当于指针与类型)
解决:移动语句函数move
#include using namespace std;class A{
public:virtual void showInfo(){cout << "i am is A" << endl;}A(const A& other){cout << "A copy" << endl;}A(){cout<< "A structure" << endl;}virtual ~A(){cout << "A destruct" << endl;}
};class B:public A{
public:void showInfo() override{cout << "i am is B" << endl;}B(const B& other){cout << "B copy" << endl;}B(){cout<< "B structure" << endl;}virtual ~B(){cout << "B destruct" << endl;}
};int main()
{A* a=new B ();a->showInfo();delete (B*)a;
// A a1=B();//B是临时变量,单纯一个临时对象,用来进行数据传递,赋值给a1时会发生拷贝,放在栈中
// static_cast(a1).showInfo();//error,原因临时对象声明短暂,仅存在一行
// a1.showInfo();//c++中的语法糖cout << "---------------------语法糖----------------" << endl;const A& a2=B();//B temp=B();(此处发生一次编译优化:RVO编译优化) const A& a2=temp;
// a2.showInfo();//在类函数中加const或者强转const_cast(a2).showInfo();//都很麻烦,所以右值引用出现//c++11的右值引用,右值是没有办法取地址的,如:常量,临时对象cout << "---------------------右值引用----------------" << endl;A&& a3=B();a3.showInfo();//c++11中的右值引用,如果想要引用左值,c++11提供了std::move(左值对象)cout << "---------------------右值引用,引用左值----------------" << endl;A&& a4=move(a3);a4.showInfo();return 0;
}
拓展:
(N)RVO(Return Value Optimization),是一种编译器优化技术,通过该技术,编译器可以减少函数返回时生成临时对象的个数,从某种程度上可以提高程序的运行效率,对需要分配大量内存的类对象其值复制过程十分友好。
当一个未具名且未绑定到任何引用的临时变量被移动或复制到一个相同的对象时,拷贝和移动构造可以被省略。当这个临时对象在被构造的时候,他会直接被构造在将要拷贝/移动到的对象。当未命名临时对象是函数返回值时,发生的省略拷贝的行为被称为RVO(返回值优化)。
RVO优化针对的是返回一个未具名对象或N(具名对象),也就是说(N)RVO的功能是消除函数返回时创建的临时对象。
编译器会把存储这个局部对象的地址和存储返回值临时对象的地址进行复用,也就是说避免了从局部对象到临时对象的拷贝操作,这就是RVO。
可以通过-fno-elide-constructors来禁用RVO。
是深拷贝的强有力的补充
逻辑:转移资源
使用条件:原对象不在使用时,移动到新的空间
#include using namespace std;class A{int *p;
public:A(){this->p=new int[1024];cout << "A structure" << endl;}~A(){if(nullptr==this->p){delete []p;}cout << "A destruct" << endl;}A(const A& other){//升级为深拷贝this->p=new int[1024];memcpy(this->p,other.p,sizeof (int[1024]));cout << "A copy" << endl;}//c++11提供了移动构造,避免空间浪费A(A&& other){this->p=other.p;other.p=nullptr;cout << "A move constructor" << endl;}void showInfo(){cout << "hello" << endl;}
};
int main()
{A a1;//此时放生移动构造
// A a2=a1;//此时出现两份,浪费空间A a2=std::move(a1);a1.showInfo();return 0;
}
c中异常处理存在问题:
#include using namespace std;float my_div(float a,float b){if(b==0){return -1;}return a/b;
}
float my_add(float a,float b){if(my_div(a,b)==-1){return -1;}return a+b+my_div(a,b);
}
int main()
{float a=10,b=-10;cout << my_add(a,b) << endl;//-1return 0;
}
c++中处理机制:
throw抛出异常关键字
#include using namespace std;float my_div(float a,float b){if(b==0){throw -1;}return a/b;
}
float my_add(float a,float b){return a+b+my_div(a,b);
}
int main()
{float a=10,b=-10;cout << my_add(a,0) << endl;return 0;
}
try{捕获throw抛出异常}catch(表达式){处理异常}语句捕获异常,并处理。
#include
#include using namespace std;class Error:public exception{
private:string error;
public:Error(string error){this->error=error;}//封装一个what函数const char * what() const noexcept override{return this->error.c_str();}
};
float my_div(float a,float b){if(b==0){throw logic_error("除数不可为0");//throw -1;}if(-10==b){throw "嘛呢,大哥" ;}if(88==b){throw Error("好好学习");}return a/b;
}
float my_add(float a,float b){return a+b+my_div(a,b);
}
int main()
{float a=10,b=-10;try {//一旦出现异常,就要跳转到catch,所以try...catch不能分开cout << my_add(a,88) << endl;//...下面语句不会被执行cout << "hello" << endl;} catch (exception& error) {//多态//catch (int error)cout << error.what() << endl;//cout << "除数不可为0" << endl;}catch(const char* c_char){cout << c_char << endl;}catch(Error& error){cout << error.what() << endl;}//这句话正常执行cout << "11111" << endl;return 0;
}
上一篇:Executor框架
下一篇:llvm-ir之核心类设计