菱形继承中虚基类,虚基表指针

虚基类

1.概念

当在多条继承路径上有一个公共的基类< 菱形继承>,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。所以虚基类的作用是为了保证虚基类在派生类中只被继承一次。

如图:A为公共基类

在这里插入图片描述

类D中有两个int a,这不仅没有意义,还浪费空间,显然是不合理的,所以引入虚基类,来解决这个问题。

2.声明格式

class A
{
	int a;
public:
	A(int x = 0) :a(x) {}
};
class B : virtual public A
{
	int b;
public:
	B(int x = 1) :A(x + 10), b(x) {}
};
class C : virtual public A
{
	int c;
public:
	C(int x = 2) :A(x + 10), c(x) {}
};
class D : public B, public C
{
	int d;
public:
	D(int x = 3) :B(x + 10), C(x + 5), d(x) {}
};

这样类D中就只有一份int a,可以有效的避免二义性,空间浪费。

虚继承与继承的区别:

多了一个虚基指针。
虚基类位于派生类存储空间的最末尾。
不会共用虚函数指针。

3.菱形虚继承的对象的内存分布图

虚指针所指向的虚基表的内容:

  1. 虚基指针的第一条内容表示的是该虚基指针距离所在的子对象的首地址的偏移
  2. 虚基指针的第二条内容表示的是该虚基指针距离虚基类子对象的首地址的偏移

b的内存分布图

c的内存分布图

d的内存分配图

在这里插入图片描述
2.

4虚拟继承中派生类对象的构造过程:

在派生类对象的创建中,首先是虚基类的构造函数并按它们声明的顺序构造。第二批是非虚基类的构
造函数按它们声明的顺序调用。第三批是成员对象的构造函数。最后是派生类自己的构造函数被调用。

//测试代码
class A
{ 
    int a;
public:   
    void display();
    A(int x = 5) :a(x) { cout << "Creat A " << endl; }
    ~A() { cout << "Destroy A" << endl; }
};
class B : virtual public A
{
    int b;
public: 
    B(int x = 1) :A(x) { cout << "Creat B " << endl; }
    ~B() { cout << "Destroy B" << endl; }
};
class C : virtual public A
{ 
    int c;
public:  
    C(int x = 0) :A(x) {cout << "Creat C " << endl;}
    ~C() { cout << "Destroy C" << endl; }
};
class D : public B, public C
{
    int d;
public: 
    D() :A(10), B(), C() { cout << "Creat D " << endl; }
    ~D() { cout << "Destroy D" << endl; }
};
int main()
{
    D d;
    return 0;
}

结果如图:

在这里插入图片描述

可以看到析构顺序和构建顺序相反,这是因为系统给函数分配的栈空间遵循后进先出规则

<think>嗯,用户问的是C++拟继承中的组成元素,除了偏移量还有什么。首先,我需要回顾一下继承和本概念。根据引用[2],继承用于解决菱形继承中的数据冗余问题,确保子类只有一份间接父类的数据。应该和函数类似,存储一些必要的信息来支持继承的实现。 用户已经知道存储偏移量,但想知道还有什么其他内容。我记得在继承中,虚基类子对象的位置是在派生类对象中动态确定的,因此需要某种机制在运行时计算偏移。这可能涉及到中存储不止一种偏移量。例如,可能存在两种偏移量:一种是派生类对象到虚基类子对象的偏移,另一种可能是其他相关结构的偏移,比如函数指针虚基类的偏移? 另外,可能还需要存储一些类型信息或者额外的标志位来帮助处理多个虚基类的情况。不过,C++标准并没有规定具体的实现细节,这取决于编译器。常见的实现方式,比如在Visual C++中,可能包含多个偏移量,用来处理不同层次的继承关系。例如,虚基类可能在派生类对象的不同位置,因此需要不同的偏移量来计算正确的地址。 还需要考虑指针本身的位置。在继承的类中,通常会在对象布局中添加一个指针,指向对应的中的每个条目可能对应不同的用途,比如第一个条目是当前派生类到虚基类的偏移,第二个条目可能是顶层的类偏移,或者其他辅助信息。 不过,有没有可能除了偏移量之外,还包含其他类型的信息?例如,RTTI(运行时类型信息)的指针?但通常RTTI信息是存储在函数中的,而不是中。所以可能主要还是偏移量相关的数据。 总结一下,可能的组成元素包括: 1. 虚基类子对象相对于当前对象的偏移量。 2. 虚基类子对象相对于完整对象的偏移量(在多层继承中可能需要)。 3. 可能有多个偏移量来应对不同的继承层次结构。 需要验证这些猜想是否正确。根据引用[1]中的示例代码,菱形拟继承的情况下,D类的对象d1包含B和C两个类,而它们都继承自A。在这种情况下,B和C各自的可能存储了到A的偏移量。当D对象被构造时,B和C的指针可能指向各自的,其中存储了从B或C到A的偏移量。此外,可能还有其他偏移量用于处理不同的情况,比如函数的存在或不同层次的结构。 因此,回答用户的问题时,应该指出除了偏移量之外,可能还包含其他偏移量,具体取决于编译器的实现,但通常主要存储的是偏移量信息,没有其他复杂的数据结构。不同的编译器可能有不同的实现方式,但核心功能是提供正确的偏移以定位虚基类子对象。</think>在C++拟继承中,(Virtual Base Table)是编译器用于管理虚基类子对象位置的核心机制。根据典型编译器的实现(如Visual C++和GCC),除了存储**偏移量**外,还可能包含以下元素: 1. **多层级偏移量** 中可能包含**两个偏移量**: - **当前派生类到虚基类子对象的偏移量**:用于在访问虚基类成员时,从当前对象地址计算虚基类子对象的实际地址。 - **虚基类子对象到完整对象的偏移量**:在多层继承中,帮助确定虚基类在最终派生类中的位置[^1][^2]。 例如,菱形继承时,`B`和`C`的分别存储从`B`或`C`到`A`的偏移量,而`D`的可能需要额外的偏移量支持。 2. **指针的间接寻址** 本身通过**指针(vbptr)** 访问,该指针存储在派生类对象中。的条目通过索引访问,例如: - 第一个条目:当前类到虚基类的偏移量 - 第二个条目:虚基类到完整对象的偏移量(若有需要)。 3. **编译器特定的元数据** 某些编译器可能在中添加**标志位或类型信息**,用于调试或动态类型识别(RTTI),但这类信息通常与函数(vtable)结合使用,而非独立存储在中。 ### 示例分析 以菱形继承为例: ```cpp class D : public B, public C { int d; }; ``` - `B`和`C`各自包含指针,指向其。 - `B`的存储从`B`到`A`的偏移量,`C`同理。 - 当通过`D`访问`A::a`时,需通过`B`或`C`的找到`A`的位置[^2]。 ### 总结 的核心内容是**偏移量**,但具体实现可能包含多个偏移量以支持复杂继承关系。不同编译器可能有细微差异,但均围绕动态定位虚基类子对象展开。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值