我的情况是退出软件时偶尔产生这种错误,也算是多线程中的一个使用不当的情况。
简化后的模型如下:
class A { public: virtual void vf() = 0; void fa() { vf(); } ~A() { fa(); } private: Thread *thread; }; class AA : public A { virtual void vf() { /*do something*/ } }; int main() { AA *aa = new AA; delete aa; return 0; }
没错就是在基类的构造/析构函数中对虚函数进行了调用。实际的情况是:线程T1和T2持有同一个AA对象的指针,当T1在对AA进行析构,执行到A::~A()中时,T2还在跑着AA::vf(),因为一旦进入A::~A()后,对象的虚表指针已经被重新指向了基类A的虚表,不再是AA的了,然后在这个间隙里T2产生了对vf()的调用,就抛出异常了。
遇到这个情况,简单的做法是先在AA析构之前先让线程T2终止。然而软件将线程安置在基类中(如上面A::thread),要求派生类去执行停止线程动作,这样便会使得接口的易用性下降,因为很有能会忘记执行了,就跟忘记释放内存一样,在不知不觉中又引回了这个问题。
最后还是去掉了vf的纯虚属性,变成一个实现为空的普通虚函数了。