gpt4 book ai didi

c++ - 为什么可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?

转载 作者:行者123 更新时间:2023-11-28 02:57:29 25 4
gpt4 key购买 nike

我不明白为什么下面的代码示例有效,希望得到一些说明。在我看来,由于 derivedMethodDerived 的非静态方法,它应该只能从实例化的 Derived 对象(或指向一个)的指针。但是,通过将指向实例化的 Base 对象的指针转换为指向 Derived 的指针,可以调用 derivedMethod。为什么?

代码:

// compiled with gcc
#include <iostream>
using namespace std;

struct Base { };
struct Derived : Base
{
void derivedMethod() { cout << "foo" << endl; }
};

int main()
{
Base *basePtr = new Base();
((Derived *)basePtr)->derivedMethod();
delete basePtr;

return 0;
}

输出:

foo

编辑:

在发布这个问题之前,我修改了 Derived 以包含一个整数成员,然后我在 derivedMethod 中输出它。它仍然可以编译和运行,没有任何错误。

编辑:

我意识到这不是好的 C++ 编码风格。这只是关于为什么我提供的代码示例有效的问题,因为它模仿了我在野外找到的代码。

最佳答案

当您使用强制转换时,您说“将其设为 Derived *”实际上是在欺骗编译器。由于编译器随后必须按照您的要求进行操作[并且在某些情况下,代码更复杂,您可能确实想要这样做,因为您知道指针确实是指向 Derived 的指针,只是目前你只有一个 Base * 指针]。

然而,执行此操作的“正确”方法是使用 dynamic_cast<Derived *>(basePtr) ,如果转换无效,它将返回 NULL。所以像这样:

Derived *dPtr = dynamic_cast<Derived *>(basePtr);
if (dPtr != NULL)
{
dPtr->derivedMethod();
}

现在,这是安全的,因为如果 basePtr 没有指向有效的 Derived 类,结果将是 NULL ,并且不会进入调用 derivedMethod 的代码。

另请注意,就目前而言,当您在代码中调用 derivedMethod 时,根本无法保证实际会发生什么。它可能会崩溃,也可能会“工作”。 (在这个简单的示例中,编译器甚至可能会检测到这种情况并给出错误 - 但它不必这样做,这是因为在更复杂的情况下,编译器无论如何都无法检测到它)。

此外,使用 Derived 中的成员变量可能会或可能不会导致可检测的问题。这完全取决于 Base 返回的 new Base() 对象“之后”是什么——它可能是那里的“未使用空间”(在这种情况下,一切都“按预期工作”[就好像你实际上已经为 Derived 对象分配了额外空间——但是当然这个值没有被初始化,所以不要对例如 std::string 这样做,它需要在安全使用之前进行构造],或者它可能是重要的东西,所以如果你写入那个位置,事情就会变得非常错误(例如delete basePtr 崩溃是因为你的代码覆盖了 delete 需要的东西)。但我们谈论的是未定义的行为,并且编译器/运行时系统在这里几乎可以做任何事情,“没有”在技术上是错误的,无论多么奇怪。如果代码决定用 1000 位小数打印圆周率、播放音乐或崩溃。C 和 C++ 都有大量的情况,规范说“在这种情况下发生的事情是未定义的”。这主要是因为它可能很难/昂贵 [ 1] 检测情况并做一些“有意义的”事情。

请注意,这可能“不起作用”:

#include <iostream>
using namespace std;

struct Base { };
struct Derived : Base { int y; void derivedMethod() { cout << "foo" << endl; y = 77; } };

int main()
{
Base b;
int x;
Base *basePtr = &b;
x = 42;
((Derived *)basePtr)->derivedMethod();

cout << "x=" << x << endl;
return 0;
}

现在,它可能会在此处显示 x = 77。也有可能没有。完全取决于编译器的作用。

[1] 例如,在某些处理器中,编译器必须添加 50 条额外指令来检查错误。在另一个处理器上,它是一个额外的指令,所以还不错。但是生产第一个需要 50 条额外指令来检查某些东西的处理器的公司肯定不希望检查这个错误。

关于c++ - 为什么可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21653230/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com