关于C++里”Pure Virtual Function Called”的问题

前几天项目里遇到一个crash的bug,直接原因code执行时报了一个”pure virtual function called!”的错,然后挂了。
直觉上来说pure virtual function是在编译阶段就会报错的,不应该出现这样的问题。但问题既然出现了,必然是某个地方真的调用到了纯虚函数。这个问题挺有趣,搞定之后决定记录下来。

Google之,第一个结果就是最完美地解释这个问题产生的原因的。http://www.artima.com/cppsource/pure_virtual.html 不过这里稍微再解释一下。

产生这个问题的原因主要有二:
1) 在基类的构造函数里调用了纯虚函数,这个很容易理解,很显然俺们的项目里不会有这种低级错误,否则一跑就crash… 总之,这个错误就忽略了;
2) 某个继承类的对象调用一个虚函数时,这个对象已经被析构了,这时可能是内存错误,也可能是pure virtual function called

具体点,上code:

class Base
{
public:
  virtual ~Base() { sleep(1); }
  virtual void DoIt() const = 0;
};

class Derived : public Base
{
public:
  virtual ~Derived() { /*sleep(1);*/ }
  virtual void DoIt() const {
    std::cout<<"Derived::DoIt()"<<std::endl;
  }
};

void* Task(void* const p)
{
  const Base * const b = reinterpret_cast<const Base*>(p);
  while (1) {b->DoIt(); usleep(50000);}
  return NULL;
}

int main()
{
  pthread_t t;
  Derived * const b = new Derived();
  pthread_create(&t, NULL, Task, (void*)b);

  delete b;
  pthread_join(t, NULL);
}

执行结果(gcc版本4.6.1):

pure virtual method called
terminate called without an active exception
Aborted

稍微解释一下:
1) Base的析构函数里会sleep 1秒;
2) 线程里不停地调用 b->DoIt()函数;
3) 在delete b之后,继承的类Derived已经析构,而Base还存在,因此这时去调用DoIt()时就出现了pure virtual method called的error

如果想做backtrace,可以实现自己的handler。在gcc里,这个函数是__cxa_pure_virtual,自己实现一份

extern "C"
void __cxa_pure_virtual () {
  std::cout<<"In My Pure Virtual Call!"<<std::endl;
}

执行结果是:

In My Pure Virtual Call!
In My Pure Virtual Call!
...

用gdb加个断点,看backtrace就搞定了。

Share