向上转换非指针对象时,vtable 指针会发生什么?

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

我在大学学习c++的VTable。也许我错过了一些东西,但我看到了以下示例:

class Parent{
public:
    int x;
    Parent(int _x) : x(_x){}
    virtual void print() const{
        cout << x << '\n';
    }
};
class Derived : public Parent{
public:
    int y;
    Derived(int _x, int _y) : Parent(_x), y(_y){}
    void print() const override{
        cout << x << ' ' << y << '\n';
    }
};
int main()
{
    Derived *a = new Derived(1, 2);
    Parent *b = a;
    b->print();
}

这里我了解到b的VTable指针更改为a的VTable指针,结果打印“1 2”,这似乎是合理的。 但我仍然不明白为什么如果我对非指针做同样的事情它就不起作用:

class Parent{
public:
    int x;
    Parent(int _x) : x(_x){}
    virtual void print() const{
        cout << x << '\n';
    }
};
class Derived : public Parent{
public:
    int y;
    Derived(int _x, int _y) : Parent(_x), y(_y){}
    void print() const override{
        cout << x << ' ' << y << '\n';
    }
};
int main()
{
    Derived a(1, 2);
    Parent b = a;
    b.print();
}

为什么它打印“1”,我实际上想知道的是VTable指针会发生什么。 b的VTable指针不会变成a的VTable指针吗?为什么?

谢谢你。

c++ pointers vtable
1个回答
0
投票

我想知道为什么没人敢写答案:-)
我简单总结一下:

简短的回答是“虚函数表指针没有发生任何事情”。

每个具有虚方法的类或从具有虚方法的类派生的类都有自己的 vtable,并且该类的每个实例都有一个指向其自己类的 vtable 的指针。
实例内部的 vtable 指针永远不会改变。

关键要素是

Parent
Derived
的 vtable 看起来相同(它们指向
print
实现),但调用者不需要知道指针指向哪里。

在第一个示例中,您只有一个实例,但有两个指向同一实例的类型化指针。
类型化指针不知道目标实例的确切类型,但它们知道在实例 vtable 内部哪里可以找到指向正确

print
方法的指针。

在第二个示例中,您创建了两个实例,它从 b 的内容复制

构造
a
(仅
Parent
字段)。
由于构造的
b
Parent
,因此该实例也会获得自己的 vtable,该 vtable 与
Parent
相匹配。

如果您从两个变量中

print
并让
print
方法输出
this
指针的地址

,可能会更清楚
#include <iostream>
using namespace std;

class Parent{
public:
    int x;
    Parent(int _x) : x(_x){}
    virtual void print() const{
        cout << x << " at " << this << '\n';
    }
};
class Derived : public Parent{
public:
    int y;
    Derived(int _x, int _y) : Parent(_x), y(_y){}
    void print() const override{
        cout << x << ' ' << y << " at " << this << '\n';
    }
};
int main()
{
    Derived *pa = new Derived(1, 2);
    Parent *pb = pa;

    Derived a(3, 4);
    Parent b = a;

    pa->print();
    pb->print();
    a.print();
    b.print();
}

输出示例:

1 2 位于 0xd062b0
1 2 位于 0xd062b0
3 4 位于 0x7ffd557ea6a0
3 在 0x7ffd557ea690

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