我正在尝试下面的代码:
我用opengascap()
指针调用汽车类的car*
指向nuclear**
类的对象,但它给出了对象的nuclear***
函数正在指出。
我的问题是为什么它给出输出“火”,虽然函数名在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;
}
输出:火
答案 0 :(得分:4)
您指向类型为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机制是一个实现细节 - 它在编译器中很常见,但它没有以任何方式强制执行,并且编译器可以自由地以不同方式实现它 - 所以你不应该指望它在你的程序中。)