父类与子类间的继承关系
一、父类与子类
父类与子类的相互转换
1、派生类的对象可以赋给基类,反之不行
2、基类的指针可以指向派生类,反之不行
3、基类的引用可以初始化为派生类的对象,反之不行
4、派生类指针必须强制转换为基类指针后才可以指向基类
5、基类指针转换为派生类指针容易导致崩溃性错误
6、虚基类的引用或派生不能转换为派生类
class father{
//
};
class son : public father{
//
};
int main() { father f; son s; f = s;//正确 s = f;//错误 father *pf = new son;//正确 son *ps = new father;//错误 father &rf = s;//正确 father &rs = f;//错误 return 0; }
继承关系对基类成员的影响
公有成员 | 保护成员 | 私有成员 | |
公有继承 | 公有 | 保护 | 不可访问 |
保护继承 | 保护 | 保护 | 不可访问 |
私有继承 | 私有 | 私有 | 不可访问 |
成员函数 | 1 | 1 | 1 |
对象 | 1 | 0 | 0 |
子类 | 1 | 1 | 0 |
1:可以访问 0:不可访问
当所有成员都变成不可访问时,再往下派生就没有意义了。
二、子类的构造与析构
1、构造派生类对象时,先执行基类的构造函数,再执行子类的构造函数,析构反之
class father { public: father(){cout<<"father construct"<<endl;} ~father(){cout<<"father delete"<<endl;} }; class son : public father { public: son(){cout<<"son construct"<<endl;} ~son(){cout<<"son delete"<<endl;} }; int main() { son s; return 0; }
输出:
father construct son construct son delete father delete
2.如果是多重继承,基类的构造顺序按给定的顺序,析构反之
class father { public: father(){cout<<"father construct"<<endl;} ~father(){cout<<"father delete"<<endl;} }; class mother { public: mother(){cout<<"mother construct"<<endl;} ~mother(){cout<<"mother delete"<<endl;} }; class son : public father, public mother { public: son(){cout<<"son construct"<<endl;} ~son(){cout<<"son delete"<<endl;} }; int main() { son s; return 0; }
输出:
father construct mother construct son construct son delete mother delete father delete
3.利用基类的构造函数构造子类,效率更高
class father { int x; public: father(int a):x(a){cout<<"father construct:"<<x<<endl;} }; class son : public father { int y; public: son(int a, int b):father(a), y(b){cout<<"son construct:"<<y<<endl;} }; int main() { son s(1, 2); return 0; }
输出:
father construct:1 son construct:2
三、多重继承
1.多重继续的二义性,根本原因是
假如A有Test(),则B和C都有Test(),于是D产生了二义性
class A { public: void Test(){cout<<"A"<<endl;} }; class B { public: void Test(){cout<<"B"<<endl;} }; class C : public A, public B { }; int main() { C c; c.Test(); //错误 c.A::Test(); //正确,输出:A c.B::Test(); //正确,输出:B return 0; }
2.编译器通常都是从离自己最近的目录树向上搜索的
子类的Test()覆盖了基类的Test(),并不代表基类的Test()消失,只是不能直接访问
class A { public: void Test(){cout<<"A"<<endl;} }; class B { public: void Test(){cout<<"B"<<endl;} }; class C : public A, public B { void Test(){cout<<"C"<<endl;} }; int main() { C c; c.Test(); //正确,输出:C c.A::Test(); //正确,输出:A c.B::Test(); //正确,输出:B return 0; }
3.对于单一继承,子类能否访问父类的父类,只与继承的方式有关
对于多重继承,子类不能直接访问父类的父类。
4.用virtual来避免二义性。
class B : virtual public A.
四、继承与包含
1.一个类的成员变量列表中包含另一个类的对象,叫做包含(包容)。
2.包含与私有继承的区别:
包含:
1)使程序看上去更清晰易懂
2)不存在继承带来的问题
3)可以包括另一个类的多个对象
私有继承:
1)可以访问基类的保护成员
2)可以重定义虚函数,实现多态