调用纯虚函数 [英] Calling pure virtual function

查看:230
本文介绍了调用纯虚函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


可能的重复:

在构造函数中调用虚函数


看看这段代码。
在Base类的构造函数中,我们可以使用'this'指针调用纯虚函数。现在,当我想创建一个类型的指针到同一个类,并将this转换为同一类型。它抛出运行时异常纯虚函数调用异常。为什么会这样?

  #include< iostream> 

using namespace std;

class Base
{
private:
virtual void foo()= 0;
public:
Base()
{
//取消注释以下2行并且不工作(运行时异常)
// Base * bptr = Base *)this;
// bptr-> foo();
// this call works
this-> foo();
}
};

void
Base :: foo()
{
cout<< Base :: foo()= 0< endl;
}

class Der:public Base
{
public:
Der()
{
}
public:
void foo()
{
cout< Der :: foo()< endl;
}
};

int main()
{
cout< 你好,世界! << endl;
Der d;
}


解决方案

调用构造函数中的虚函数。



虚函数不按您认为的方式调度。相反,在构建期间,正在构建的基本子对象的动态类型是基本类型,因此函数将被分派到基本函数(在您的情况下是纯虚拟的)。



只是不要这样做。



(原因很明显:当构造派生对象时,子对象必须首先被构造,因此环境派生对象在基础构造时不存在。)






编辑:这里有一些更多的解释。完全允许和鼓励编译器静态地执行虚拟分派 。在这种情况下,在编译时确定已经调用了哪个实际函数。当您在中说出 foo() this-> foo()基础构造函数,或者在其他上下文中 x.Base :: foo() / code>是你的对象。当调度发生静态时,则直接调用 Base :: foo()的实现,或者如果没有实现,则会出现链接器错误。

另一方面,如果动态地发生调度,即在运行时,则尽管不寻常,但是调度实际上结束了选择 code> Base :: foo()作为最终目标。这在正常条件下不会发生,因为编译器不会让你用纯虚函数来实例化一个类,所以普通动态调度的目标总是一个实现必须存在的函数(或至少你如果你不链接它会得到一个链接器错误。)



但还有一个情况,这是有问题的:编译器决定执行调度在运行时,无论什么原因,和dipatch结束在一个纯虚函数。在这种情况下,程序终止。它是无关的函数是否实现,但是在多态类层次结构中没有一个条目(认为它是一个空指针在vtable,因此 = 0 )。为了实现这一点,对象的动态类型必须是抽象基类的类型必须动态发送。前者只能在派生对象的基础构造函数中实现,而后者需要你说服编译器不静态分派调用。这是 this-> foo()(static)和 Base * p = this; (< code> x.Base :: foo(),这是)



所有这些仅仅是实现的结果,当然是由未定义的行为覆盖的。如果你想远离它,一个事情,那就是动态调度找不到一个纯虚函数。当然,你不能在构造函数中调用虚函数。


Possible Duplicate:
Calling virtual functions inside constructors

Look at this code. In the constructor of Base class, we can call the pure virtual function using 'this' pointer. Now when I want to create a typed pointer to the same class and casting 'this' to the same type. It throws run time exception 'pure virtual function call exception'. Why is this so ?

#include <iostream>

using namespace std;

class Base
{
  private:
  virtual void foo() = 0;
  public:
  Base()
  {
    //Uncomment below 2 lines and it doesn't work (run time exception)
    //Base * bptr = (Base*)this;
    //bptr->foo();
    //This call works
    this->foo();
  }
};

void
Base::foo()
{
  cout << "Base::foo()=0" << endl;
}

class Der : public Base
{
  public:
  Der()
  {
  }
  public:
  void foo()
  {
    cout << "Der::foo()" << endl;
  }
};

int main()
{
  cout << "Hello World!" << endl;
  Der d;
}

解决方案

You must never call virtual functions within a constructor.

Virtual functions don't get dispatched the way you think they do. Rather, during construction, the dynamic type of the base subobject that is being constructed is the base type, and thus the function is dispatched to the base function (which is pure-virtual in your case).

Just don't do it.

(The reason is obvious: When constructing a derived object, the base subobject must necessarily be constructed first, so the ambient derived object doesn't even exist at the time of the base construction.)


Edit: Here's some more explanation. Compilers are perfectly allowed and encouraged to perform virtual dispatch statically if they can do so. In that case, it is determined at compile time already which actual function will be called. This happens when you say foo() or this->foo() in the Base constructor, or when you say x.Base::foo() in some other context where Derived x; is your object. When the dispatch happens statically, then either the implementation of Base::foo() is called directly, or you get a linker error if there is no implementation.

On the other hand, if the dispatch happens dynamically, i.e. at runtime, then there is a possibility, albeit unusual, that the dispatch actually ends up picking Base::foo() as the final target. This cannot happen under "normal" conditions, because the compiler won't let you instantiate a class with pure-virtual functions, and so the target of an ordinary dynamic dispatch is always a function for which an implementation must exist (or at least you'd get a linker error if you don't link it).

But there is one more situation, which is the one in question: The compiler decides to perform the dispatch at runtime, for whatever reason, and the dipatch ends at a pure-virtual function. In that case your program terminates. It is irrelevant whether the function is implemented or not, but is simply doesn't have an entry in the polymorphic class hierarchy (think of it as a "null pointer in the vtable", hence the = 0). For this to happen, the dynamic type of the object must be the type of an abstract base class, and the dispatch has to happen dynamically. The former can only be achieved inside the base constructor of a derived object, and the latter requires you to convince the compiler not to dispatch the call statically. This is where the difference between this->foo() (static) and Base * p = this; p->foo(); (dynamic) comes in. (Also contrast this to x.Base::foo(), which is dispatched statically.)

All this is merely a consequence of the implementation and covered by the blanket "undefined behaviour", of course. If you want to take one thing away from it, then it is that dynamic dispatch cannot find a pure-virtual function. And of course that you must never call virtual functions within a constructor.

这篇关于调用纯虚函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆