访问其中一个成员时,是否存在指向基类子对象的隐式指针?

时间:2017-09-18 12:57:30

标签: c++ inheritance this name-lookup data-members

如果我们有这个代码:

class Base
{
int anint;
float afloat;
};
class Derived : Base
{
//inherited member variables...
};

我被告知Base的成员将继承到Derived,而Derived中的继承成员实际上位于Base的基类子对象内(但是这个子对象是未命名的);在Derived中创建了Base的子对象,用于保存继承的成员。因此,当访问类中的成员时,除非您显式执行某些操作,否则会对this指针进行隐式调用,但是在访问继承对象时是否还有一个隐式指针(或任何内容)?就像,如果我们通过anintDerived的实例中访问derivedInstance->anint,这实际上看起来像derivedInstance->this->somethingToBaseObjectThatHoldsTheMembers->anint或者这是如何工作的?

2 个答案:

答案 0 :(得分:1)

没有指向基类数据的特殊指针。 C ++中的数据成员布局紧跟C语言。(事实上,您的示例没有虚拟方法,因此必须完全遵循C)。因此,让我们考虑您的代码在C中的外观:

 struct Base
 {
    int anint;
    float afloat;
 };
 struct Derived
 {
   struct Base; // C style inherit from struct Base
   int a2ndInt;
 };

在C中,结构内存布局被定义为与编写它时非常相似。这意味着struct Derived基本上具有以下内存布局。

struct Derived
{
    int anint;
    float afloat;
    int a2ndInt;
};

指针指向结构的开头,因此从指向Derived或Base的指针访问 anint afloat 涉及相同的内存偏移。因此,羽绒在这里总是很容易。

当你拥有虚函数时,事情会变得更复杂,因为数据结构必须有一个指向其虚函数的隐藏指针,但只需要一个这样的指针。让我们考虑单继承的情况,你可以想象一个类似的布局(实际布局取决于ABI):

 struct Base
 {
    <ABI defined pointer type> * class_; // hidden virtual function table
    int anint;
    float afloat;
 };
 struct Derived
 {
   struct Base; // inherit from struct Base
   int a2ndInt;
 };

现在struct Derived可能具有以下内存布局。请注意,在创建Derived对象时,构造函数必须设置 class _ 指针。这是构造函数从Base类构造函数开始的一个原因,因为每个Derived类都可以覆盖 class _ 指针。

struct Derived
{
    <ABI defined pointer type> * class_; 
    int anint;
    float afloat;
    int a2ndInt;
};

再次从指向Derived或Base的指针访问 anint afloat 涉及相同的偏移量。因此,向下铸造在这里也很容易。

多重继承要复杂得多,而且static_cast&lt;&gt;羽绒铸造是必不可少的。这种情况最接近你的想法,但仍然只涉及到一个这个指针的偏移量。

struct Base1
{
   <ABI defined pointer type> * class_; // hidden virtual function table
   int anint;
};
struct Base2
{
   <ABI defined pointer type> * class_; // hidden virtual function table
   float afloat;
};

我对ABI不太熟悉,但我想隐藏的虚拟表指针可能以某种方式合并,导致内存布局如下:

struct Derived
{
    <ABI defined pointer type> * class_; // merged Base1 and Base2
    int anint;
    float afloat;
    int a2ndInt;
};

或未合并(取决于ABI)

struct Derived
{
    <ABI defined pointer type> * class_; // from Base1
    int anint;
    <ABI defined pointer type> * class_; // from Base2
    float afloat;
    int a2ndInt;
};

因此,再次从指向Derived或Base1的指针访问 anint 涉及相同的偏移量,但访问漂浮不起作用。这意味着使用从Derived指针到Base2指针的C样式转换(即使用(Base2*))失败,您需要static_cast<>来处理偏移量的变化。

请注意,如果只有一个基类具有成员数据,则这并不复杂。这就是为什么经常建议在使用多重继承时,只有一个基类应该有数据。

注意:真正的ABI定义了结构中数据的真实布局。此处显示的内容仅用于说明目的。

答案 1 :(得分:-1)

没有。编译器选择布局(ABI)。静态强制转换利用该布局的知识来使用static_cast来调整指针。

RTTI使用dynamic_cast启用动态指针调整。

参见例如Regular cast vs. static_cast vs. dynamic_cast