c++ 虚基类

首先,我们看一个类图:


我们知道,如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员。

在一个类中保留间接共同基类的多份同名成员,虽然有时是有必要的,可以在不同的数据成员中分别存放不同的数据,也可以通过构造函数分别对它们进行初始化。但在大多数情况下,这种现象是人们不希望出现的。因为保留多份数据成员的拷贝,不仅占用较多的存储空间,还增加了访问这些成员时的困难,容易出错。而且在实际上,并不需要有多份拷贝。

C++提供虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员。假设类D是类B和类C公用派生类,而类B和类C又是类A的派生类,如图11.21所示。 设类A有数据成员data和成员函数fun;派生类B和C分别从类A继承了data和fun,此外类B还增加了自己的数据成员data_b,类C增加了数据成员data_c。如果不用虚基类,就会在类D中保留了类A成员data的两份拷贝,分别表示为int B::data和int C::data。同样有两个同名的成员函数,表示为void B::fun()和void C::fun()。类B中增加的成员data_b和类C中增加的成员dat_c不同名,不必用类名限定。此外,类D还增加了自己的数据成员data_d和成员函数fun_d。

注意: 虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。

声明虚基类的一般形式为:
class 派生类名: virtual 继承方式 基类名
即在声明派生类时,将关键字 virtual 加到相应的继承方式前面,经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次,也就是说,基类成员只保留一次。
注意:为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。
通过以上这些分析,我们大概已经知道虚基类是什么东西了,但是这并不是虚基类的全部知识点,还有以下需要了解

1 二义性问题的解决

2 虚基类的初始化问题

说明:不过这些问题的解决方案网上一大堆,相信读者很容易就能搞定。

### C++虚基类的使用方法及注意事项 #### 一、虚基类的作用 虚基类的主要目的是为了防止多重继承时产生的二义性和重复数据成员问题。当一个派生类通过多个路径继承同一个基类时,如果不使用虚继承,则会创建该基类的多个实例副本[^2]。 #### 二、虚基类的定义方式 要实现虚继承,在声明派生类时需在 `class` 关键字后面加上关键字 `virtual` 和 `public` 或其他访问修饰符。例如: ```cpp class Base {}; class Derived1 : virtual public Base {}; // 虚继承 class Derived2 : virtual public Base {}; // 虚继承 class FinalDerived : public Derived1, public Derived2 {}; ``` 在此例子中,由于 `Derived1` 和 `Derived2` 都是从 `Base` 类虚继承而来的,所以最终的 `FinalDerived` 只有一个 `Base` 的子对象[^2]。 #### 三、初始化顺序 对于虚基类,其构造函数会在任何非虚基类之前被调用。即使某个中间派生类未显式调用虚基类的构造函数,编译器也会自动为其提供默认参数来完成初始化过程。以下是具体示例代码及其解释: ```cpp #include <iostream> using namespace std; class A { protected: int value; public: A(int v = 0) : value(v) { cout << "A Constructor Called with Value: " << value << endl; } }; class B : virtual public A { public: B() : A(1) { cout << "B Constructor Called" << endl; } }; class C : virtual public A { public: C() : A(2) { cout << "C Constructor Called" << endl; } }; class D : public B, public C { public: D() : A(3), B(), C() { cout << "D Constructor Called" << endl; } }; int main(){ D obj; } ``` 在这个程序里,尽管 `B` 和 `C` 各自尝试设置不同的初始值给它们共同的虚拟父级 `A` ,但由于存在更具体的指令——即来自最底层衍生类别 `D` 所指定之数值 (此处为3),故此优先采用后者作为实际传递至 `A` 构造函式的参数[^3]。 #### 四、内存布局特点 因为引入了额外的信息用于管理共享的基础结构体实例位置关系等原因,通常情况下运用到虚基础类型的实体相较于单纯线性单一层次体系架构下的同等情况会有更大的存储需求量以及稍微复杂一点的操作逻辑[^2]。 #### 五、动态类型转换中的表现形式 如果涉及到从含有虚根节点的对象模型内部提取特定种类别的指针或者引用操作的话,那么就可能需要用到诸如 `dynamic_cast<>` 运算符来进行安全可靠的转型处理动作[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值