OOP继承问题,虚基类, 建构子的构建顺序问题
来源:互联网 发布:java编程工具安卓 编辑:程序博客网 时间:2024/05/16 20:31
这里我们说说多重继承(multiple inheritance)。 多重继承的问题涉及到建构子和析构子的调用顺序问题, 多重继承造成的歧义型(例如, 当多个base classes中有同名函数的时候, 如何解决,members from common base class如何解决(dimond的时候, 即类A继承自B, C, 但是B, C又同时继承自一个类D时候, 就出现歧义了, 此时A有两份关于D的拷贝))。 当涉及到vitual base class(虚基类)的时候, 初始化的顺序和调用顺序如何呢??
首先, 定义一个class具有多个parent class 很简单, 只需要list them one by one:
<span style="font-size:14px;">class Boo: public Bum, public Foo { // specify its own properties};</span>这样, 一个Boo类的object可以upcast(向上转型)为 Bum, 或者Foo, 因为Boo继承了其父类的一切, 虽然object无法看到父类的private的部分。
当我们创建一个Boo的object的时候, 需要先调用Bum 和 Foo的default constructor, 然后在调用自己的。 也就是说,先构造父亲的那一部分, 调用析构子的顺序和调用构造子的顺序相反。
如下:
#include <iostream>using namespace std;class Sofa {public: Sofa() { cout << "构造Sofa" << endl; } ~Sofa() { cout << "析构Sofa" << endl; } void sit() { cout << "sit" << endl; }};class Bed {public: Bed() { cout << "构造Bed" << endl; } ~Bed() { cout << "析构Bed" << endl; } void lie() { cout << "lie" << endl; }};class Sofabed: public Bed, public Sofa { //按照list的顺序构建, 即继承的顺序public: Sofabed() { cout << "构造Sofabed" << endl; } ~Sofabed() { cout << "析构Sofabed" << endl; } void sitAndLie() { cout << "sit and lie" << endl; }};int main() { Sofabed myfur; cout << endl; myfur.sit(); //继承的 myfur.lie(); //继承的 myfur.sitAndLie(); // 自己的 return 0;}运行结构如下:
可见, 先构造父类的, 然后才是自己的。 当有多个父亲的时候, 则按照继承的顺序构造出来。
但是, 多重继承有很多的缺点, 不建议使用。
首先, 如果有两个parent classes有same name的members的时候, 就不好了。 此时当要使用这些同名的函数的时候, 我们只能用resolution operator解决冲突。 很不好。
另外, 只使用单继承更加的easier, less prone to error, 所以最好不要使用多重继承啦。
因为多重继承可以改写为单继承。 例如Sofabed可以单继承Sofa, 而Sofa里面有一个Bed的class。
不建议使用多继承。
Anyway, 我们在多说一下多重继承。
派生类(derived class)必须为每一个继承的base class的建构子提供初始化的参数。 例如如下:
<span style="font-size:14px;">Cderived::Cderived(arg_B1, arg_B2, ..., argBn, arg_Cderived): B1(arg_B1), B2(arg_B2), ..., Bn(arg_Bn) { // initialize Cderived's own data members }</span>注意, 基类的建构顺序是以派生类继承的顺序调用的,而不是这里的初始化列表的顺序。
当派生类调用自己的defalut copy constructor的时候, 编译器(compiler)会自动的调用基类的default copy constructor。
当复制构造函数是你自己定义的时候, 你就必须把相对应的参数传递给基类的copy constructor。
<span style="font-size:14px;">Cderived::Cderived(Cdedrived &c1): B1(c1), B2(c1), ..., B(c1) { // copy the rest data members}</span>
多重继承造成的歧义性有如下 两种情况:
(1)如果A继承了B, 和C, 但是B, C类中定义了同一个名字的函数func(), 此时A有两个同名的函数。 解决歧义的办法是使用B::func() 和 C::func()标识, 即resolution operator。 另外, 如果我们什么基类例如B型的一个pointer ptr的时候,然后将ptr指向派生类的时候, 用ptr指向派生类A的某个成员(假如这个成员在B中也有), 此时ptr指到的不是派生类A的这个成员, 而是基类的。 因为ptr会根据自己的type(为B)去调用相关函数(尽管ptr指向的地址是A的对象)。 此时的解决办法就是把这个同时出现在A和B的成员函数声明为virtual的(虚函数)。
(2) A继承了B,和C, 但是B, C 继承了同一个D, 此时A就继承了两份D的拷贝, 形成diamond的shape。 一份来自于B, 一份拷贝来自于C. 不仅造成在调用common base class的成员函数产生的歧义性, 而且浪费了空间。 解决的办法就是使用virtual base class(虚基类)。 即将D声明为虚基类(被其他类虚继承), 这样在做common base class的时候就只有一份拷贝了。
如下:
<span style="font-size:14px;">class CA { // common base classpublic: int x; CA(int a = 0) { x = a;}};clas CB: virtual public CA {public: int y; CB(int a, int b= 0): CA(a) { y = b}; // 注意CA(a)不能省略, 下同}class CC: virtual public CA {public: int z; CC(int a = 0, int b = 0): CA(a) { z = b;}};class CD: public CB, public CC {public: int w; CD(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0):CA(a), CB(a, b), CC(c, d) { // 注意只有CA(a)对x的设置有效, 后面的CB,CC无效 w = e; } };</span>
下面设置的时候:
<span style="font-size:14px;">CD obj(5, 4, 3, 2, 1);// x 为5, 后面的设置就无效</span>宣告为虚基类的时候, CA的构造函数值执行了一次。
为了保证虚基类的正确性, 我们最好首先调用virtual base class 的constructorfirst。 这样, 后面调用徐基类的constructor就无效了。 虚基类保证虚基类的建构子只执行一次。 (继承发生了, 才有虚基类的概念)。
建构子的初始化问题:
(1) 首先调用virtual class 的 建构子,有多个虚基类的时候, 按照虚基类的声明顺序, 而不是初始化顺序。
(2)然后按照普通的基类的声明顺序调用建构子(即继承关系声明的顺序)。
例如下例:
<span style="font-size:14px;">#include <iostream>using namespace std;class C1 {public: C1() {cout << "建构C1\n";} ~C1() {cout << "析构C1\n";}};class C2 {public: C2() {cout << "建构C2\n";} ~C2() {cout << "析构C2\n";}};class C3 {public: C3() {cout << "建构C3\n";} ~C3() {cout << "析构C3\n";}};class C4 {public: C4() {cout << "建构C4\n";} ~C4() {cout << "析构C4\n";}};class CD: public C3, virtual public C4, virtual public C2 { // 宣告顺序private: C1 obj; // C1 的objectpublic: CD(): obj(), C2(), C3(), C4() {cout << "建构CD\n";}// 初始化顺序 ~CD() {cout << "析构CD\n";}};int main() { CD dd; cout << "here\n" << endl; return 0;}</span>
执行结果如下:
可见建构的顺序是: 先虚基类, 多个虚基类按照宣告的顺序。 然后是其他的普通基类, 按照宣告的顺序, 最后是自己的成员里面的对象建构(如果有class作为其成员, 按照宣告的顺序), 最后调用自己的析构函数,
如果改为里面有static 的class的object的时候, 无论如何要先把static的最先构造出来, 如果这个static又继承自某个父类, 同理, 先构造器父类, 如下:
<span style="font-size:14px;">#include <iostream>using namespace std;class C1 {public: C1() {cout << "建构C1\n";} ~C1() {cout << "析构C1\n";}};class C2 {public: C2() {cout << "建构C2\n";} ~C2() {cout << "析构C2\n";}};class C3 {public: C3() {cout << "建构C3\n";} ~C3() {cout << "析构C3\n";}};class C4 {public: C4() {cout << "建构C4\n";} ~C4() {cout << "析构C4\n";}};class CD: public C3, virtual public C4, virtual public C2 { // 宣告顺序private: C1 obj; // C1 的object C2 obj2; static C3 obj3; public: CD(): obj(), C2(), C3(), C4() {cout << "建构CD\n";}// 初始化顺序 ~CD() {cout << "析构CD\n";}};C3 CD::obj3; // 奇怪, 如果没有这一行, 即注释掉, 则obj3不会被构造出来!!!!!int main() { CD dd; cout << "here\n" << endl; return 0;}</span>运行如下:
- OOP继承问题,虚基类, 建构子的构建顺序问题
- 【OOP】PHP下解决多继承的问题
- Java对象的建构顺序
- 继承时候类的执行顺序问题
- python3 中多继承的顺序问题
- 持续更新--JSP网站建构中遇到的问题
- 子进程继承锁的问题
- 子list中的顺序会影响list的顺序问题
- 深入分析Java对象的建构顺序
- 深入分析Java对象的建构顺序
- 类在继承关系上的初始化的顺序问题
- 对象的初始化顺序问题02(继承中)
- 对象的初始化顺序问题03(继承中)练习
- java继承时候类的执行顺序问题
- Java继承关系中静态代码块的顺序问题
- angular的指令的子作用域继承问题
- 关于子查询的执行顺序的问题
- ActivityGroup的子activity响应back事件的顺序问题
- 计算机视觉领域的牛人博客和研究机构
- OC学习小结之ios运行过程详解
- HDU 1152. False Mirrors(DFS)
- git之获取开源项目
- Node.js笔记(六)不使用页面模板渲染界面
- OOP继承问题,虚基类, 建构子的构建顺序问题
- 最牛辞职信
- 【算法】九种常见排序
- Java线程的创建
- 第二周项目1旱冰场造价
- 利用中根序列和后根序列重建二叉树
- Hostker云主机
- [阿牛的牛肉串]
- 工厂模式