Skip to content

关于本章

本章讨论pure virtual method called异常,目前遇到了两种场景:

  • 多线程race condition导致pure virtual method被调用了
  • constructor、destructor中调用pure virtual method

多线程竞争导致pure virtual method called

在开发过程中偶现这个问题,大致场景如下:

使用一个网络库,这个网络库是基于callback模式的,该库需要developer创建继承自接口类ICallbackDerived类,并override其中的OnConnectOnReceiveOnClose等方法;类IConnection表示网络连接,它的主要method:

1、Create(ICallback* Callback) 创建连接对象

2、Send(const void* Data, int32_t Len) 发送数据

对于每个network connection,需要调用其Create方法向其注册ICallback类对象。这样关于这个network connection对象的消息,底层网络库就通过回调ICallback对象的成员方法通知到用户;

ICallback是接口类,所以它将OnConnectOnReceiveOnClose等方法都声明为pure virtual method;

这个网络库运行于一个独立的thread,用户线程调用IConnectionSend方法发送数据;网络库thread执行回调函数;

这种模式(多线程+不清晰的ownership of object),带来的一个问题是:对Derived类对象的lifetime的管理的麻烦;如果programmer删除了Derived类对象(显然,会调用destructor),而底层网络库又继续使用这个对象,显然这就会导致问题,由于存在数据竞争,所以这多种可能:

  • Derived类对象被完全析构了,则导致dangling pointer
  • Derived类对象被部分析构,则导致pure virtual method
  • ......

解决方法是:需要等OnClose后(OnClose表示底层网络库不再使用该对象),object析构掉;

下面这篇文章中反映的情况大致和我遇到的场景类似;

tombarta GCC pure virtual method called

I spent about two hours today trying to debug a race condition in a multi-threaded C++ app today… definitely not a fun thing to do.

At one point, I saw this error pop up:

pure virtual method called
terminate called without an active exception
Aborted

What? I know I can’t instantiate a class that has any pure-virtual methods, so how did this error show up? To debug it, I decided to replace all of the potentially-erroneous pure virtuals with stub functions that printed warnings to stderr. Lo and behold, I confirmed that polymorphism wasn’t working in my application. I had a bunch of Deriveds sitting in memory, and yet, the Base methods were being called.

Why was this happening? Because I was deleting objects while they were still in use. I don’t know if this is GCC-specific or not, but something very curious happens inside of destructors. Because the object hierarchy’s destructors get called from most-derived to least-derived, the object’s vtable switches up through parent classes. As a result, at some point in time (nondeterministic from a separate thread), my Derived objects were all really Bases. Calling a virtual member function on them in this mid-destruction state is what caused this situation.

NOTE: "deleting objects while they were still in use",需要联系上下文来理解:作者描述的场景是在一个multiple thread中,一个thread,删除了另外一个thread正在使用的object;

上面这一段中的最后一句话揭示了根本原因所在:Calling a virtual member function on them in this mid-destruction state is what caused this situation.

Here’s about the simplest example I can think of that reproduces this situation:

#include <pthread.h>
#include <unistd.h>
struct base
{
    virtual ~base() { sleep(1); }
    virtual void func() = 0;
};
struct derived : public base
{
    virtual ~derived() { }
    virtual void func() { return; }
};
static void *thread_func(void* v)
{
    base *b = reinterpret_cast<base*>(v);
    while (true) b->func();
    return 0;
}
int main()
{
    pthread_t t;
    base *b = new derived();
    pthread_create(&t, 0, thread_func, b);
    delete b;
    return 0;
}

NOTE: 编译: g++ test.cpp -lpthread,运行:

pure virtual method called
terminate called without an active exception
已放弃(吐核)

So what’s the moral of the story? If you ever see the error message pure virtual method called / terminate called without an active exception, check your object lifetimes! You may be trying to call members on a destructing (and thus incomplete) object. Don’t waste as much time as I did.

如何修复?

  • 使用virtual method来替换pure virtual method
  • 避免race condition

constructor、destructor中调用pure virtual method

Examples

Constructor call pure virtual method

#include <iostream>

class BaseWithPureFunction
{
public:
    BaseWithPureFunction()
    {
        CallPureFunc();
        // 此处调用了纯虚拟函数,该虚拟函数由派生类
        //实现,但此处派生类还未构造成功,会导致r6025
        //错误
    }
    virtual void PureFunc() = 0;


    void CallPureFunc()

    {
         PureFunc();

    }
};

class  Derived: public  BaseWithPureFunction
{
public:

    virtual void PureFunc()

    {
         std::cout<<"Derived::PureFunc"<<std::endl;;
    }
};

int main()
{
    Derived d;
}

编译: g++ test.cpp,输出如下:

pure virtual method called
terminate called without an active exception
已放弃(吐核)

Destructor call pure virtual method

#include <iostream>

class BaseClassWithDestructorCallPureFun
{
public:
    virtual void PureFunc() = 0;

    void CallPureFunc()
    {
        PureFunc();
    }

    virtual ~BaseClassWithDestructorCallPureFun()
    {
        std::cout << "~BaseClassWithDestructorCallPureFun destructor call" << std::endl;

        CallPureFunc();
    }
};

class Derived: public BaseClassWithDestructorCallPureFun
{
public:
    virtual void PureFunc()
    {
        std::cout << "PureFunc Call From Derived" << std::endl;
    }

    virtual ~Derived()
    {
        std::cout << "~Derived destructor call" << std::endl;
    }
};

void Test()
{
    Derived d;
}
int main()
{
    Test();
}

编译: g++ test.cpp,输出如下:

~Derived destructor call
~BaseClassWithDestructorCallPureFun destructor call
pure virtual method called
terminate called without an active exception
已放弃(吐核)

TODO

wiki.c2.com Pure Virtual Function Called