为什么要提供衍生版本?

时间:2012-08-01 06:13:15

标签: c++ inheritance virtual

我正在尝试下面的代码:

我用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;
 }
  

输出:火

1 个答案:

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