“纯虚函数调用”崩溃从何而来?

我有时会注意到程序在我的电脑上出现错误:“纯虚函数调用”。

当一个对象不能创build一个抽象类时,这些程序如何编译?

如果您尝试从构造函数或析构函数进行虚函数调用,可能会导致这种情况。 既然你不能从构造函数或析构函数中调用虚拟函数(派生类对象还没有被构造或已被销毁),它会调用基类的版本,在纯虚函数的情况下,版本不会不存在。

( 在这里看现场演示)

class Base { public: Base() { doIt(); } // DON'T DO THIS virtual void doIt() = 0; }; void Base::doIt() { std::cout<<"Is it fine to call pure virtual function from constructor?"; } class Derived : public Base { void doIt() {} }; int main(void) { Derived d; // This will cause "pure virtual function call" error } 

除了从具有纯虚拟函数的对象的构造函数或析构函数调用虚函数的标准情况,如果在对象被销毁之后调用虚函数,还可以获得纯虚函数调用(至less在MSVC上) 。 很明显,这是一个非常糟糕的事情,但是如果你正在使用抽象类作为接口,那么你可能会看到一些东西。 如果你使用引用计数的接口,并且你有一个引用计数错误,或者你在multithreading程序中有一个对象使用/对象销毁竞争条件,那么它可能更有可能……关于这种纯调用的事情是通常不太容易弄清楚发生了什么事情,因为检查虚拟通话的“通常的嫌疑人”将会清理干净。

为了帮助debugging这些types的问题,您可以在各种版本的MSVC中replace运行时库的purecall处理程序。 你通过提供你自己的function来完成这个签名:

 int __cdecl _purecall(void) 

并在链接运行时库之前进行链接。 这使您能够控制在检测到纯呼叫时发生的情况。 一旦你有了控制权,你可以做一些比标准处理程序更有用的东西。 我有一个处理程序,可以提供purecall发生的地方的堆栈跟踪; 看到这里: http : //www.lenholgate.com/blog/2006/01/purecall.html更多的细节。

(注意你也可以调用_set_purecall_handler()来在一些MSVC版本中安装你的处理程序)。

通常当你通过悬挂指针调用一个虚拟函数时 – 很可能实例已经被破坏了。

也可能有更多“创造性”的原因:也许你已经设法切断了实现虚拟function的对象部分。 但是通常只是这个实例已经被销毁了。

我猜想有一个为抽象类创build的vtbl由于某些内部原因(可能需要某种types的运行时types信息),并且出现错误,并且真正的对象会得到它。 这是一个错误。 那应该说那是不可能发生的事情。

纯粹的猜测

编辑:看起来像我在错误的情况下有问题。 OTOH IIRC有些语言确实允许vtbl调用离开构造函数的析构函数。

我使用VS2010,每当我尝试直接从公共方法调用析构函数,我会在运行时得到一个“纯虚函数调用”错误。

 template <typename T> class Foo { public: Foo<T>() {}; ~Foo<T>() {}; public: void SomeMethod1() { this->~Foo(); }; /* ERROR */ }; 

所以我把里面的东西搬到了Foo()里去分开私人的方法,然后就像魅力一样工作了。

 template <typename T> class Foo { public: Foo<T>() {}; ~Foo<T>() {}; public: void _MethodThatDestructs() {}; void SomeMethod1() { this->_MethodThatDestructs(); }; /* OK */ }; 

这是一个偷偷摸摸的方式来发生。 我今天有这个事情发生在我身上。

 class A { A *pThis; public: A() : pThis(this) { } void callFoo() { pThis->foo(); // call through the pThis ptr which was initialized in the constructor } virtual void foo() = 0; }; class B : public A { public: virtual void foo() { } }; B b(); b.callFoo();