前几天项目里遇到一个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还存在,
如果想做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就搞定了。