XY从X和Y继承。将XY *转换为X *,然后转换为Y *,然后调用Y的函数会导致调用X的函数

问题描述 投票:0回答:2
#include <iostream>

struct X
{
    virtual void x() = 0;
};

struct Y
{
    virtual void y() = 0;
};

struct XY : X, Y
{
    void x() override { std::cout << "X\n"; }
    void y() override { std::cout << "Y\n"; }
};

int main()
{
    XY xy;

    X* xptr = &xy;

    Y* yptr = (Y*)xptr;

    yptr->y(); //prints "X"....

    ((Y*)((X*)(&xy)))->y(); // prints "Y"....
}

输出:

X
Y

有人可以详细解释为什么会这样吗?为什么第一个呼叫正在打印X,以及为什么两个呼叫彼此不同?

c++ casting multiple-inheritance virtual-functions
2个回答
1
投票

如评论中所述,就语言而言,这是未定义行为。

但是,实际选择的行为的确揭示了典型C ++编译器的内部功能是如何工作的,因此,调查为什么得到输出结果仍然很有趣。但是,请务必记住,以下解释不是通用的。并没有对以这种方式工作的硬要求,并且任何依赖这种方式的代码都有效地破坏了,[[即使它在您尝试过的所有编译器上都可以工作。

C ++多态性通常使用vtable实现,它基本上是功能指针的列表,可以看作对象中的隐藏成员指针。

so

struct X { virtual void x() = 0; }; struct Y { virtual void y() = 0; };

大致相当于(它实际上并不使用std::function<>,但这使伪代码更清晰易懂::

struct X { struct vtable_t { std::function<void(void*)> first_virtual_function; }; vtable_t* vtable; void x() { vtable->first_virtual_function(this); } }; struct Y { struct vtable_t { std::function<void(void*)> first_virtual_function; }; vtable_t* vtable; void y() { vtable->first_virtual_function(this); } };

注意X::vtable_tY::vtable_t如何

偶然地

基本上是同一件事。如果XY具有不同的虚拟功能,那么事情就不会整齐地排列。难题的另一个重要部分是多重继承实际上是一个串联:

struct XY : X, Y { void x() override { std::cout << "X\n"; } void y() override { std::cout << "Y\n"; } }; // is roughly equivalent to: struct XY { static X::vtable vtable_for_x; // with first_virtual_function assigned to XY::x() static Y::vtable vtable_for_y; // with first_virtual_function assigned to XY::y() X x_base; Y y_base; XY() { x_base.v_table = &vtable_for_x; y_base.v_table = &vtable_for_y; } void x() { std::cout << "X\n"; } void y() { std::cout << "Y\n"; } };

这意味着从多重继承的类型转换为基数不仅是更改指针类型的问题,

value

也必须更改。 X指针等效于基础对象指针,Y指针实际上是

不同地址

X* xptr = &xy; // is equivalent to X* xptr = &xy->x_base; Y* xptr = &xy; // is equivalent to Y* xptr = &xy->y_base;
最后,当您从X转换为Y时,由于这些类型无关,因此该操作为reinterpret_cast,因此尽管该指针可能是指向Y的指针,但基础对象仍然是[ C0]。

幸运的是,您可以排队:

    X和Y都具有vtable指针作为第一个成员对象。
  • X和Y的vtable实际上等效,前者指向X,后者指向XY::x()。>>
  • 因此,当将调用XY::y()的逻辑应用于类型y()的对象时,这些位恰好排成一行以调用X

2
投票
[XY::x()执行Y* yptr = (Y*)xptr;
© www.soinside.com 2019 - 2024. All rights reserved.