警告信息:deleting object of abstract class type ‘xxx’ which has non-virtual destructor will cause undefined behavior [-Wdelete-non-virtual-dtor]
问题情境
有虚基类A,拥有一个纯虚成员函数func,B类以公共方式继承A:
class A
{
public:
virtual void func()=0;
};
class B:public A
{
public:
void func()
{
...
}
}
接着在测试函数中使用基类A指针指向派生类B的实例化对象,并在最后delete该基类指针:
void test()
{
A* a=new B;
...
delete a;
}
上述代码在编译过程中出现开头的警告信息。
错误原因
警告信息中说到我们删除具有非虚析构函数的指针将会导致未定义的行为,我们首先看一下这样会造成什么后果。
我们在虚基类A和派生类B中自定义析构函数在被调用时打印文本信息:
class A
{
public:
~A()
{
cout<<"A类析构"<<endl;
}
virtual void func()=0;
};
class B:public A
{
public:
~B()
{
cout<<"B类析构"<<endl;
}
void func()
{
...
}
}
执行完test之后屏幕上只输出了一行“A类析构”,而没有打印“B类析构”的信息。
也就是说,如果基类中的析构函数不是虚函数的话,在销毁指向派生类对象的基类指针时,不会调用派生类的虚构函数,这样就可能出现内存未释放的问题,因此产生警告。
解决方案
正如警告信息所说的,我们应将基类的析构函数写为虚析构,但注意,虚析构函数是不可以只声明而不定义的,所以我们不能将基类的虚析构函数仅仅写为virtual ~A()=0;
而不实现,可以这样写:
class A
{
public:
virtual ~A()
{
cout<<"A类析构"<<endl;//删掉这一句改为空实现也可以
}
virtual void func()=0;
};
(或者类内定义virtual ~A()=0;
,并在类外空实现也是可以的。)
我们再次执行测试函数:
这时可以看到基类和派生类的析构函数全部调用,警告解决。