为什么基指针可以在虚拟功能中访问派生成员变量

时间:2018-04-26 21:09:49

标签: c++ polymorphism

class Base {
public:
    virtual void test() {};
    virtual int get() {return 123;}
private:
    int bob = 0;
};

class Derived: public Base{
public:
    virtual void test() { alex++; }
    virtual int get() { return alex;}
private:
    int alex = 0;
};
Base* b = new Derived();
b->test();

当调用testget时,会传入隐式this指针。是不是因为Derived类具有与内存布局相同的子内存布局纯基础对象,然后this指针作为基指针和派生指针工作?

另一种说法是,Derived的内存布局就像

vptr <-- this
bob
alex

这就是为什么它可以在alex中使用b->test(),对吧?

3 个答案:

答案 0 :(得分:2)

Derived方法内部,隐式this指针始终是Derived*指针(更一般地说,this指针总是< / em>匹配被调用的类类型。这就是Derived::test()Derived::get()可以访问Derived::alex成员的原因。这与Base无关。

Derived对象的内存布局以Base的数据成员开头,后跟可选填充,后跟Derived的数据成员。这允许您在期望Derived对象的任何地方使用Base对象。当您将Derived*指针传递给Base*指针或Derived&Base&引用的引用时,编译器将在编译时相应地调整指针/引用指向Base对象的Derived部分。

在运行时调用b->test()时,bBase*指针,编译器知道test()virtual并将生成访问b vtable中的相应位置,并调用指向的方法。但是,编译器并不知道派生对象类型b在运行时实际指向的是什么(这是多态的整个魔力),因此它无法自动调整隐式{{1}在编译时指向正确的派生指针类型。

如果this指向b个对象,则Derived的vtable指向b的vtable。编译器知道从Derived开始Derived开始的确切偏移量。因此,Base vtable中test()的插槽将指向编译器生成的私有存根,以将隐式Derived指针调整为Base *this指针然后跳转到Derived *this的实际实现代码。

在幕后,大致(不完全)实现如下伪代码:

Derived::test()

实际细节涉及更多,但这应该让您基本了解多态如何在运行时分派虚拟方法。

答案 1 :(得分:1)

您所展示的内容相当准确,至少对于典型的实施方式而言。它不能保证精确地显示它(例如,编译器可能很容易在bobalex之间插入一些填充,但无论哪种方式它“知道”alex处于一些预定义的偏移,因此它可以指向Base,从中计算正确的偏移量,并使用那里的东西。

不是你提出的问题,所以我不会试图详细说明,但只是一个公平的警告:当/如果涉及多个继承时,计算这样的偏移可能/确实会变得更复杂一些。与访问最派生类的成员不同,但是如果访问基类的成员,它必须基本上计算到该基类开头的偏移量,然后添加偏移量以获得正确的偏移量那个基类。

答案 2 :(得分:0)

派生类不是单独的类,而是扩展。如果某事被分配为派生,那么指针(它只是内存中的地址)将能够从派生类中找到所有内容。程序集中不存在类,编译器根据内存中的分配方式跟踪所有内容,并相应地提供相应的检查。