C++基础 虚继承

本文详细介绍了C++中的虚继承和非虚继承概念。非虚继承会导致类的多个副本,而虚继承则能解决二义性问题,确保类只有一个基类的成员。通过实例代码展示了两种继承方式的使用和效果,强调了虚继承在处理多继承时的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

c++之中一个类不能被多次说明为一个派生类的直接基类,但可以多次成为间接基类。

一 非虚继承类

例子:

class B
{
    public:
    int x.....
}
class A1:public B
{
    .....
}
class A2:public B
{
    ......
}
class D:public A1,public A2
{
    ......
}

可以看出,D类继承了A1和A2,而A1和A2继承了B ,也就是说D类有两个B类的成员副本,所以称B是非虚基类。

非虚继承测试:

#include<iostream>
using namespace std;
class B
{
    public:
    B()
    {cout<<"construct called:B\n";}
    ~B()
    {cout<<"destructor called:B\n";}
    int b;
};
class B1:public B
{
    public:
    B1() {cout<<"constuct called:B1\n";}
    ~B1() {cout<<"destruct called:B1"<<endl;}
    int b1;
};
class B2:public B
{
    public:
    B2() {cout<<"constuct called:B2\n";}
    ~B2() {cout<<"destruct called:B2"<<endl;}
    int b2;    
};
class D:public B1,public B2
{
    public:
    D() {cout<<"constuct called:D\n";}
    ~D() {cout<<"destruct called:D"<<endl;}
    int d;
};
void test()
{
    D dd;
    dd.B1::b=5;
    dd.B2::b=10;
    dd.b1=25;
    dd.b2=111;
    dd.d=149;
    cout<<"dd.B1::b="<<dd.B1::b<<"dd.B2::b"<<dd.B2::b<<"dd.b1="<<dd.b1<<"dd.b2="<<dd.b2<<"dd.b"<<dd.d<<endl;
}
int main()
{
    test();
}

运行结果可以自己去试着运行看一下。

运行后第一轮输出的字符,如“construct called:B”等可以看出运行的顺序。

而void test()里面它直接用D dd,并且输出有d1,d2,d,b,而class D里只是定义了int d;所以显而易见是因为继承了B1和B2。还有要注意,为了访问不同版本的d,必须要用作用域指名是那个基类。

虚继承

当我们为了避免产生二义性,直接想用一个dd.b版本的时候,我们就需要把B1和B2对 B的继承说明为虚继承,它的说明形式是在类继承的关键字前面加一个virtual。这样就不用分别再建立dd.B1:: b,dd. B2::b,而是有virtual指引建立指向dd.b的指针。比如,让我们对上面的例子进行改动,利用virtual。

#include<iostream>
using namespace std;
class B
{
    public:
    B()
    {cout<<"construct called:B\n";}
    ~B()
    {cout<<"destructor called:B\n";}
    int b;
};
class B1:virtual public B
{
    public:
    B1() {cout<<"constuct called:B1\n";}
    ~B1() {cout<<"destruct called:B1"<<endl;}
    int b1;
};
class B2:virtual public B
{
    public:
    B2() {cout<<"constuct called:B2\n";}
    ~B2() {cout<<"destruct called:B2"<<endl;}
    int b2;    
};
class D:public B1,public B2
{
    public:
    D() {cout<<"constuct called:D\n";}
    ~D() {cout<<"destruct called:D"<<endl;}
    int d;
};
void test()
{
    D dd;
    dd.B1::b=5;
    dd.B2::b=10;
    dd.b1=25;
    dd.b2=111;
    dd.d=149;
    cout<<"dd.B1::b="<<dd.b<<"dd.B2::b"<<dd.b<<"dd.b1="<<dd.b1<<"dd.b2="<<dd.b2<<"dd.b"<<dd.d<<endl;//这里有改动
}
int main()
{
    test();
}

运行后我们会发现不会出翔二义性了,对象就只有一个b数据成员了。

最后说一句,一个类体系中可以作为虚继承和非虚继承取决于对他的继承方式,于基类本身的定义无关。

### C++ 中的虚继承 #### 虚继承概念 当多个基类共同继承自同一个基础时,可能会遇到重复继承同一祖先的情况。为了避免这种冗余并解决由此产生的二义性问题,在C++中引入了虚继承机制[^1]。 #### 实现方式 声明一个作为另一个的虚基类只需要在`class`定义中的冒号后面加上关键字`virtual`即可。例如: ```cpp class A {}; // 基础 class B : virtual public A {}; class C : virtual public A {}; ``` 这里的关键在于`virtual`修饰符使得无论是经由B还是C来访问A成员都不会造成冲突或歧义,因为子D只持有一个唯一的A对象副本而不是两个独立的对象副本。 #### 使用场景 对于复杂的层次结构特别是存在多条路径通往相同根节点的情形下特别有用。比如在一个图形库里面可能既有圆形也有矩形都属于形状这一大别;而填充颜色的功能既可以应用于所有的几何体也可以单独作用于某些特定型的图形之上。此时采用虚继承可以有效简化程序逻辑防止不必要的麻烦发生[^2]。 #### 示例代码 下面给出一段简单的示例展示如何利用虚继承构建安全可靠的体系: ```cpp #include <iostream> using namespace std; // 定义最顶层的基础 class Animal { public: string name; Animal(const char* n):name(n){} }; // 定义中间层抽象接口 class Mammal : virtual public Animal{ protected: bool hasFur; public: Mammal(const char *n, bool f) :Animal(n),hasFur(f){}; void showInfo(){ cout << "This is a mammal named "<< this->name<<endl; if(hasFur){ cout<<"It has fur."<<endl; }else{ cout<<"It does not have fur."<<endl; } } }; // 另一条分支上的具体实现 class Pet : virtual public Animal{ private: int age; public: Pet(const char*n,int a):Animal(n),age(a){} void displayAge(){ cout <<this->name <<"is"<< age <<"years old."<< endl; } }; // 继承自两条不同线路的具体动物种 class Dog : public Mammal , public Pet{ public: Dog(const char*n,bool f,int a):Mammal(n,f),Pet(n,a){} }; int main() { Dog dog("Rex",true,5); dog.showInfo(); dog.displayAge(); return 0; } ``` 上述例子展示了通过虚继承建立起来的一组相互关联却又互不影响的数据模型。其中Dog既可以从Mammal那里获得哺乳动物特有的属性又能够从Pet这边得到宠物的相关信息而不必担心两者之间可能存在任何潜在的竞争关系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

daydr

来吧,尽情的打赏我啊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值