单继承
1.继承的写法和权限问题
class 子类:继承方法 父类
继承方法:父类中的属性在子类中的最低权限
权限由低到高:public
protected
private
eg:class boy:public woman
——woman类的属性在boy类的最低权限为public
父类的私有属性对于子类来说是不可访问的
类的访问权限有三种:
- public 公共权限: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问
- protected 保护权限: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问
- private 私有权限:只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问。
三种权限的区别:
public:可以被任意实体访问
protected:只允许本类及子类的成员函数访问
private:只允许本类的成员函数访问
2.继承中构造函数的写法
子类的构造函数必须调用父类的构造函数
不想写的时候,习惯在父类中增加一个无参构造函数
如:直接Boy boy;
——报错,显示是已删除的构造函数
解决方法:在woman类中新增无参构造函数Woman(){}
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include <iostream> using namespace std; class Woman { public: Woman(string name,int age):name(name),age(age){ cout<<"调用基类带参构造函数"<<endl; } Woman(){ cout<<"调用基类无参构造函数"<<endl; }; void print(){ cout<<"Woman "<<name<<" "<<age<<endl; } protected: string name; int age; }; class Boy:protected Woman { public: Boy(){ cout<<"调用子类无参构造函数"<<endl; } Boy(string name,int age):Woman(name,age){ cout<<"调用子类带参构造函数"<<endl; } void print(){ cout<<"Boy "<<name<<" "<<age<<endl; } protected: }; int main(){ Woman woman1; cout<<"============================"<<endl; Woman woman2("Amy",40); cout<<"============================"<<endl; Boy boy1; cout<<"============================"<<endl; Boy boy2("Jack",10); cout<<"============================"<<endl; woman2.print(); boy2.print(); };
|
结果:

总结:子类构造函数一定会调用对应的父类构造函数
构造顺序和析构顺序
正常情况下,构造函数和析构函数顺序相反
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <iostream> using namespace std; class A{ public: A(){ cout<<"A"; } ~A(){ cout<<"~A"; } }; class B:public A{ public: B(){ cout<<"B"; } ~B(){ cout<<"~B"; } }; int main(){ A a; B b; };
|
结果:
注意:有delete
的时候
1 2 3
| B *p=new B; B b; delete p;
|
结果:
类的继承的遗传性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class A{ public: int a; A(int a):a(a){} }; class B{ public: int b; B(int a,int b):A(a),b(b){} }; class C{ public: int c; C(int a,int b,int c):B(a,b),c(c){} };
|
- B继承A,C继承B——则C不仅包含B的属性,也包含A的属性
- 构造函数的写法:初始化参数列表—调用直接父类的构造函数+初始化其他属性
继承中的同名问题
父类和子类同名问题:
- 数据成员同名
- 成员函数同名
通常情况为就近原则,优先调用本类的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <iostream> using namespace std; class Woman { public: Woman(string name,int age):name(name),age(age){} void print(){ cout<<"Woman "<<name<<" "<<age<<endl; } protected: string name; int age; }; class Boy:public Woman { public: Boy(string name,int age,string mmName,int mmAge):name(name),age(age),Woman(mmName,mmAge){} void print(){ cout<<"Boy "<<name<<" "<<age<<endl; cout<<"类名限定 "<<Woman::name<<" "<< Woman::age<<endl; } protected: string name; int age; }; int main(){ Boy coolboy("boy",18,"woman",40); coolboy.print(); coolboy.Woman.print(); Woman *p=new Boy("Woman_Boy",19,"Woman",41); p->print(); };
|
结果:
1 2 3 4
| Boy boy 18 类名限定 woman 40 Woman woman 40 Woman WomanPtr 41
|
总结:
通常情况下,都是就近原则,优先调用本类的数据和方法
如果加上类名限定,则调用对应类的数据和方法
如果是指针,没有virtual
的情况下,也是就近原则
特殊情况:父类指针被子类对象初始化,没有virtual
的情况看类型,有virtual
的情况看对象
Woman *p=new Boy("Woman_Boy",19,"Woman",41);p->print();
所以调用的p
对应的类型Woman
的函数
注意:不允许子类指针被父类对象初始化//除非进行指针类型转换
多继承
- 多继承——存在两个及以上的父类
- 权限问题:跟单继承情况一样
- 构造函数写法:跟单继承也是一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <iostream> using namespace std; class Woman { public: Woman(string WomanFName,string WomanSName):WomanFName(WomanFName),WomanSName(WomanSName){} protected: string WomanFName; string WomanSName; }; class Man { public: Man(string ManFName,string ManSName):ManFName(ManFName),ManSName(ManSName){} protected: string ManFName; string ManSName; };
class Son:public Woman,public Man { public: Son(string ManFName,string ManSName,string WomanFName,string WomanSName,string SonSName) :Man(ManFName,ManSName),Woman(WomanFName,WomanSName),SonFName(ManFName+WomanFName),SonSName(SonSName){} void print(){ cout<<"mother:"<<WomanFName+WomanSName<<endl; cout<<"father:"<<ManFName+ManSName<<endl; cout<<"Son:"<<SonFName+SonSName<<endl; } protected: string SonFName; string SonSName; }; int main(){ Son son("欧","明明","阳","丽丽","修"); son.print(); };
|
1 2 3
| mother:阳丽丽 father:欧明明 Son:欧阳修
|
虚继承
菱形继承:B、C继承A,D继承B、C
存在二义性:例如A-int a;B-//int a;C-//int a;D-//int a;
二义性:D的a来自谁

解决方法一:加上类型限定

解决方法二:使用虚继承
1 2 3 4 5 6 7 8 9 10 11 12 13
| class A { public: int a; A(int a) : a(a) {} }; class B : virtual public A { public: B(int a) : A(a) {} }; class C : virtual public A { public: C(int a) : A(a) {} };
|


PS:几个类共享一份数据

- 类B和类C都虚继承自类A。虚继承意味着它们不会直接包含类A的实例,而是会包含一个指向类A的共享基类的指针(这通常被称为虚基类指针)。所以
sizeof(B)=8,sizeof(C)=8
- 类D继承了类B和类C,D需要包含B和C的所有成员,所以
sizeof(D)=16
虚函数
虚函数——用virtual
修饰的函数
注意:构造函数不能为虚函数
1.虚函数对类的内存的影响

2.纯虚函数
抽象类:具有纯虚函数的类
特性:不能创建对象,但可以创建对象类型指针

子类继承抽象类,必须实现所有的纯虚函数才能实例化。
3.虚析构函数

结果:
解决方法:使用虚析构函数
——注意:虚函数一旦被继承,不管被继承多少次,永远为虚函数

结果:
1 2 3 4
| 父类构造函数 子类构造函数 子类析构函数 父类析构函数
|