我正在尝试下面的代码:
我在调用 car 类的 opengascap()
时使用 car*
指针指向 nuclear**
类的对象但它赋予了所指向对象的 nuclear***
功能。
我的问题是为什么它给出输出“fire”,尽管 nuclearsubmarine
类中甚至不存在函数名称。
#include <iostream>
using namespace std;
class Vehicle
{
public:
virtual ~Vehicle() { }
virtual void startEngine() = 0;
};
class Car : public Vehicle
{
public:
virtual void startEngine()
{
cout<<"start car";
}
virtual void openGasCap()
{
cout<<"open";
}
};
class NuclearSubmarine : public Vehicle
{
public:
virtual void startEngine()
{
cout<<"start ship";
}
virtual void fireNuclearMissle()
{
cout<<"fire";
}
};
int main()
{
Car car;
Car* carPtr = &car;
NuclearSubmarine sub;
NuclearSubmarine* subPtr = ⊂
carPtr=(Car*)subPtr;
// This last line would have caused carPtr to point to sub !
carPtr->openGasCap(); // This might call fireNuclearMissle()!
return 0;
}
Ouput: fire
最佳答案
您正指向一个类型为 NuclearSubmarine
的对象,该对象的指针指向类型为 Car
。你不能那样做,因为这些类型是不相关的。
此错误会调用未定义的行为,这会导致您的程序以不可预测的方式运行。
如果您对发生这种情况的原因感兴趣,请阅读内部如何实现虚函数:Virtual method table .这将使您清楚。
回复评论:
没错。 Vehicle
实例都有一个指向 vtable 的内部指针,它看起来像这样:
0: Vehicle::~Vehicle
1: Vehicle::StartEngine // this is a null pointer
Car
实例将它们的 vptr 指向一个看起来像这样的 vtable:
0:Vehicle::~Vehicle // this one didn't get overridden
1:Car::startEngine
2:Car::openGasTrap
NuclearSubmarine
的 vtable 如下所示:
0:Vehicle::~Vehicle // this one didn't get overridden
1:NuclearSubmarine::startEngine
2:NuclearEngine::fireNuclearMissile.
如果你有这个,
Vehicle* v = new Car();
v->startEngine();
它被编译成这样的东西(前面是伪代码):
Vehicle* v = new Car();
// OK, v is a Vehicle and startEngine is a Vehicle's virtual function of index 1
// let's call it!
StartEngineFunc* f = v.VPTR[1]; // look up the vtable using the object's virtual pointer
CallMethod(v, f);
虚拟指针将函数查找指向正确的 vtable w/r/t 对象的实际运行时类型。
这允许您通过指向基类的指针调用派生类的方法,因为派生类将具有与其父类对应的虚拟表(它的第一部分)。
但是,如果两个类没有父子关系,那么它们的虚表将具有不同的含义。这就是你的情况。
(请注意,虽然 vtable 机制是一个实现细节 - 它在编译器中很常见,但它不会以任何方式强制执行并且编译器可以自由地以不同方式实现它 - 所以你不应该指望它在你的程序中。)
关于c++ - 为什么它给出派生版本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11753503/