1

我正在尝试以下代码:

在其中我使用指向类对象的指针调用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 = &sub;
     carPtr=(Car*)subPtr;
     // This last line would have caused carPtr to point to sub !
     carPtr->openGasCap();  // This might call fireNuclearMissle()!
     return 0;
 }

输出:火

4

1 回答 1

4

您使用指向 typeNuclearSubmarine的指针指向类型的对象Car。你不能这样做,因为这些类型是不相关的。

此错误会调用未定义的行为,这会导致您的程序以不可预知的方式运行。


如果您对为什么会发生这种情况感兴趣,请阅读虚拟函数是如何在内部实现的:虚拟方法表。这会让你清楚。


回复评论:

这是正确的。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 机制是一个实现细节——它在编译器中很常见,但它并没有以任何方式强制执行,编译器可以自由地以不同的方式实现它——所以你不应该在你的程序中指望它。)

于 2012-08-01T06:15:24.303 回答