【问题标题】:Casting pointer to different pointer causes wrong virtual function to be called将指针转换为不同的指针会导致调用错误的虚函数
【发布时间】:2021-10-06 19:43:28
【问题描述】:
#include <iostream>

struct A {
    virtual void a() {
        puts("A");
    }
};

struct B {
    virtual void b() {
        puts("B");
    }
};

struct C {
    virtual void c() {
        puts("C");
    }
};

struct D : public A, public B, public C {
    virtual void c() {
        C::c();
        puts("cd");
    }
};

int main() {
    A* obj = new D;

    obj->a();

    B* b = (B*)obj;
    b->b();
    C* c = (C*)obj;
    c->c();

    return 0;
}

我有这段代码,我有非虚拟多重继承。但是,当我调用主函数中的函数时,似乎调用了错误的虚函数。 而不是输出:

A
B
C
cd

它输出:

A
A
A

让我困惑的是,当我将代码更改为这样做时:

B* b = (B*)(D*)obj;
b->b();
C* c = (C*)(D*)obj;
c->c();

它输出我所期望的(见上文)。 Afaik 像这样进行双指针转换不会产生任何影响,并且会被编译器优化。但它似乎正在改变正在调用的虚函数。

有人能解释一下为什么这会改变调用的虚函数吗?

注意事项:

我在每一步都打印了指针,它们是一样的。

我想避免使用dynamic_cast(尽管它确实有效),因为它对于我需要它做的事情来说太慢了。

【问题讨论】:

  • 您不能将A* 转换为C*,它们不是相关的类。这大概是UB
  • C 风格转换不安全的另一个原因。 dynamic_cast 慢在哪个世界?它的开销不超过一次虚拟调用。
  • (B*)(D*) 转换是正确的,因为它将指针转换为指向D 的指针,并且编译器知道如何将D* 转换为B*。只有知道obj 实际上是一个指向D 类型对象的子对象的指针,您才能这样做。如果您总是知道这一点,那么演员阵容就可以了。如果你不能确定,那么dynamic_cast&lt;B*&gt;(obj) 会做对的。
  • 重新打开。关于为什么直接转换不起作用的重复是相关的,但没有说明为什么 (D*)(B*) 转换起作用。
  • 即使你不想使用dynamic_cast,你也应该使用static_cast。编译器会告诉您,您无法将 A * 转换为 C *,因此您需要分两步完成。第二个演员,也没有必要,因为可以很容易地写static_cast&lt;D *&gt;(obj)-&gt;b();

标签: c++ virtual-functions


【解决方案1】:

有人能解释一下为什么这会改变调用的虚函数吗?

通常,指针类型之间的 C 风格转换不会改变指针的值,因此不会产生任何影响。但是,有一个例外。

类与父类或子类之间的强制转换可以改变指针的值。例如:

class A
{ int a; };

class B
{ int b; };

class C : public A, public B
...

现在,指向A 类实例的指针可能与指向其a 成员的指针具有相同的值,而指向B 类实例的指针可能与指针具有相同的值到它的b 成员。指向类 C 实例的指针不能与指向其 A::a 及其 B::b 成员的指针具有相同的值,因为它们是不同的对象。

期待B* 的函数可以传递C*,因为C B。类似地,出于同样的原因,期望 A* 的函数可以传递 C*。但至少其中之一需要更改指针的值。

所以这些类型之间的强制转换改变值,其他的都是无操作的。

当然,这一切都是UB。您在不相关的类型之间进行转换,然后取消引用它们。

我想避免使用 dynamic_cast(尽管它确实有效),因为它对于我需要它做的事情来说太慢了。

这似乎很难相信。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-03
    • 1970-01-01
    • 2023-03-05
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    相关资源
    最近更新 更多