字段访问 - c ++中的多态而非多态类型

问题描述 投票:0回答:2

当一个类中包含虚函数时,编译器会为其创建一个虚拟表。假设我们有上面的代码:

class A {
public: 
int x;
};

class B : public A {
virtual void foo(){}
};

现在B有一个vtable,而A没有。如果我们有一个函数返回一个指向A的指针,它可以是A或B,我们访问该函数返回的对象的x字段。编译器如何进行调整以访问x,它必须在运行时完成,但如何?

c++ inheritance polymorphism virtual vtable
2个回答
1
投票

使用你提到的函数返回的A*指针,没有调整:它已经是A*,它直接指向一个A对象,它可以是A对象的B基类子对象。

但是,在你提到的函数的返回逻辑中可以并且通常会有一个指针值调整,它会产生一个指向A*对象的B

这是因为B通常在开始时会有一个vtable指针,因此A基类子对象不在偏移0处。必要的调整可以通过有效地执行不调整的reinterpret_cast来搞砸。这可能发生在unique_ptr的一个简单的删除器(shared_ptr更聪明,但需要付出一些代价)。


例:

struct A
{
    int x;
};

struct B: A
{
    virtual void foo(){}
    B( const int value ): A{ value } {}
};

B b_object( 42 );

auto ptr()
    -> A*
{
    return &b_object;
}       

#include <iostream>
auto main()
    -> int
{
    using namespace std;
    cout << "&b_object  = " << &b_object << ".\n";
    cout << "As A* it's = " << ptr() << ".\n";
    cout << b_object.x << " in B, is " << ptr()->x << " in A.\n";
}

在Windows 10中使用MinGW g ++ 7.3.0的结果:

&b_object  = 0x512030.
As A* it's = 0x512038.
42 in B, is 42 in A.

您可以从地址中看到,对于这个64位编译器,B开头的额外信息是8个字节,与该信息是8字节指针值一致。


0
投票

当函数返回指向A的指针时,如果它是A实例的基础子对象的B或类C的某个数据成员或数组D的元素的某个独立实例,则擦除所有信息。在所有这些情况下,指针指向具有成员Ax类型的正确对象。

当它是B实例的基础子对象时没有什么特别的,因为B(及其可能的vtable指针)在A之外。 A本身没有任何虚函数,所以它不能被运行时识别为B的基础子对象。

© www.soinside.com 2019 - 2024. All rights reserved.