关于虚基类和多继承

c++和pyhon都是允许多继承的。

 

但由于被继承的多个父类可能直接或间接继承自一个基类,那么此时就有可能会产生一个结果,在此定义的class中包含多个同一基类的多个副本,这种情况在大多数时候是没有意义的,反而会在逻辑上造成二义性的错误。

 

举个栗子

class A{public:void func(){}};

class B: public A{};

class C: public A{};

class D: public B, public C{};

 在上述的例子中,D间接通过B和C继承了A,那么此时D中就有了两个A的实例副本。

当然在上面的代码中是不存在语法错误的,因为不同副本中的func函数虽然定义相同,但是属于不同的作用域,所以不冲突,but...

//如果我一不小心....

D dd;

dd.func();

调用func函数的时候就会出现二义性错误,因为多继承的名字的查找是并行的。

//关于上述问题的解决方案,也是有许许多多的.
//1. 使用域作用符莱明确调用.
    D dd; dd.C::func();

//2. 在子类中重写func.
    class D:public B,public C{public: void func();};

//3. 在D中使用using引入基类中的func,明确指定.
    class D:public B, public C{public: using B::func;};
    //注意这里只能引入名字func而不是特定形参的func().
    //其实本质上而言,这里引入其实就是一个声明。
    //因为父类的作用域包含子类的作用域.
    //这样一下引入, 就频闭了其他父类中的func.

//4. 在D中使用作用域符引入func明确指定.
    class D:public B, public C{public: B::func;};
    //在高版本c++语法中抛弃这一语法.

上面的例子中在公共基类中没有定义数据成员,如果定义了数据成员,那么,很多时候是冗余的,没有必要的,如果需要数据的多个副本,直接定义子对象即可。

此外,我们谈点正事儿,虚基类。

虚基类是什么?

继承时访问级别使用virtual关键字的基类被称为虚基类,这种方式成为虚继承。

class A{
    public:
        void func(){}
};

class B: virtual public A{
    public:
        void func(){}
};

//A为虚基类,上述就是虚继承.

虚基类的作用?

在多继承时,虚基类只有一个副本,可避免引起二义性错误。

 

#include<iostream>


class A{
    public:
        void func(){
            
            std::cout<<"-----------A----------"<<std::endl;
        }
};

class B: virtual public A{
    public:
        void f(){
            
        }
};

class C: virtual public A{
    
};

class D: public C, public B{
    
};


int main(int argc, char** argv){
    
    D d;
    d.func();
    
    return 0;
}

这里A拥有默认构造函数,所以一切正常。

 

那么,深入思考一下。

我们来考虑一个问题:

按照构造函数构造对象的顺序:先构造基类在构造子类,这样迭代推理,上面的D,首先将构造过程交给,B和C,同理,B和C又会同时去构造A,而在非虚继承中,A有两个副本,B,C各自构造一个,不冲突,但虚继承中,A只有一个副本,那B,C同时去构造,不久冲突了吗?

试想一下,在现实生活中,一群小孩分一块蛋糕,当发生冲突的时候,是否是会选择一个第三方的“公平”的 “仲裁者” 来决定蛋糕的划分?成年世界也同是。 

所以,我们这里解决虚继承过程中的构造冲突的方案是:将虚基类的构造交给最终子类来在其构造函数的初始化列表中进行,如果没有在初始化列表中显式调用虚基类的构造函数,则默认调用虚基类的默认构造函数来初始化。如果虚基类莫得默认构造函数就出错。我在上面的例子中使用的就是默认构造函数初始化虚基类。

 

其次,应当记住,如果最终子类不是虚基类的直接子类,那么最终子类的中间父类对虚基类的初始化将被忽略。

 

#include<iostream>


class A{
    public:
        A(int a):a(a){}
        void func(){
            std::cout<<"a = "<<a<<std::endl;
            std::cout<<"-----------A----------"<<std::endl;
        }
    private:
        int a;
};

class B: virtual public A{
    public:
    B():A(20){}
        void f(){
            
        }
};

class C: virtual public A{
    public:
        C():A(30){}
};

class D: public C, public B{
    public:
        D():A(10){}
};


int main(int argc, char** argv){
    
    
    D d;
    d.func();
    
    return 0;
}


//Results:
a = 10
-----------A----------

 

最后,虚基类会很大程度上降低c++的编译时的性能,慎用 。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值