为什么可以从实例化基类对象的投射指针调用非静态派生类方法
Why can a non-static derived class method be called from a casted pointer to an instantiated base class object?
我不明白下面的代码示例为什么有效,我想澄清一下。在我看来,由于derivedMethod
是Derived
的非静态方法,它应该只能从实例化的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++编码风格。这只是一个关于我提供的代码示例为什么有效的问题,因为它模仿了我在野外发现的代码。
使用强制转换时,您说"Make this aDerived *
"实际上是在欺骗编译器。由于编译器必须执行您要求的操作[当然,在某些情况下,对于更复杂的代码,您可能确实想要执行此操作,因为您知道指针实际上是指向Derived
的指针,只是目前您只有一个Base *
指针]。
然而,"正确"的方法是使用dynamic_cast<Derived *>(basePtr)
,如果转换不起作用,它将返回NULL。这样的东西:
Derived *dPtr = dynamic_cast<Derived *>(basePtr);
if (dPtr != NULL)
{
dPtr->derivedMethod();
}
现在,这是安全的,因为如果basePtr
没有指向有效的Derived
类,结果将是NULL
,而不会进入调用derivedMethod
的代码。
还要注意的是,就目前情况来看,根本不能保证在代码中调用derivedMethod
时会发生什么。它可能崩溃,也可能"工作"。(在这个简单的例子中,编译器甚至可能检测到情况并给出错误,但它不必这样做,这是因为在更复杂的情况下,编译器无论如何都无法检测到它)。
此外,在Derived
中使用成员变量可能会也可能不会导致可检测的问题。这完全取决于new Base()
返回的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条额外指令来检查某些东西的处理器的公司肯定不希望检查这个错误。
因为此时此刻您有一个Derived*
指针。编译器应该如何知道你没有?如果您想在运行时检查是否真的是这样,您必须使用dynamic_cast
。如果像代码片段中那样重新解释将Base*
强制转换为Derived*
,则会得到未定义的行为,并且如果derivedMethod
访问Derived
的任何成员,则程序可能会崩溃。
class方法独立于实例化的对象存储,与对象的交互实际上只是与对象实现的接口的交互。因此,在需要访问类(属性)中的一些存储数据之前,调用类方法不会与类的实例化交互。
通过将基类指针强制转换为派生类指针,它现在假设引用的对象实现了派生类接口,并且非常乐意调用引用基类对象的派生类方法。
然而,正如其他人所说,这会导致未定义的行为。由于基类没有实现派生类的接口,因此可以假设数据存在于它实际上不存在的地方。在您的示例中,这两个对象即使在实例化时,在技术上也是抽象的,因为没有与它们相关联的数据。如果它们各自具有不同的属性,那么方法可能会引用错误的属性或完全无关的内存。
- 为什么使用 "this" 指针调用派生成员函数?
- 当使用通配符和null指针调用函数时,对输出的说明
- 使用共享指针的函数调用,其对象应为 const
- 如何调用指针类型的方法(禁用多态性)?
- C++:这是使用整数变量作为函数调用指针的正确方法吗
- 正确的方法是什么?调用指针到指针到指针内部的函数?
- 参数的混合值,当我调用指针成员函数时
- 调用指针时违反 C++ Typedef 访问冲突
- 调用指针参数从const函数指向的对象的操作员
- 为什么调用指针后给出错误的值
- 正在调用指针和引用方法
- 从c++线程调用指针
- 如何在c++中调用指针成员变量的构造
- Fortran调用c++指针函数
- 递归中修改调用指针的问题
- 如何在类定义中调用指针成员函数
- 在矢量中调用指针函数的方法
- 在类中调用指针函数
- 调用指针到成员函数
- 如何使用QtConcurrent::run调用指针作为参数的非类成员函数