non-virtual的析构函数
class Base{
public:
Base(){
cout << "Base" << endl;
}
~Base(){
cout << "~Base" << endl;
}
};
class Derive : public Base{
public:
Derive(){
cout << "Derive" << endl;
}
~Derive() {
cout << "Derive" << endl;
}
};
int main() {
Base* bp = new Derive();
delete bp;
return 0;
}
- 当我们删除(delete)一个指向派生类的基类指针时,如果该基类是non-virtual的析构函数,那么就会产生未定义行为。
- 往往的结果是,Base的析构函数会被调用,而Derive的析构函数不会被调用。
- 这是数据局部销毁和内存泄漏的方法之一(笑)。
为了使得堆上new的数据被正常释放,我们应该为用于多态的基类写一个virtual析构函数。
你可能会感到疑惑,基类和子类的析构函数名字不一样,为什么能是虚函数?
- 编译器在编译时,会将所有的析构函数名称全部换成destructor,由此完成多态。
尽量不要为非Base类声明虚函数
- 如果你的类不是用于Base类,那么最好不要为它写virtual的析构函数。
- 因为一旦写入virtual的析构函数,编译器就会引入虚表指针等一系列手段,使得每个对象的大小增加。该类产生的每个对象的结构也会随之变化,这样该类的对象就无法用于相同声明的其他语言的对象(比如C语言,因为它们没有虚表指针)。
- 这样降低了代码的移植性。
- 所以,不要为非Base类写virtual析构函数。
怎么样判断你的类是否要作为Base呢?
- 这里有一个小技巧,虽然不是每次都准。
- 如果你的类中除了析构函数,没有virtual函数,那么就不要为析构函数增加virtual。
不要继承没有virtual析构函数的多态类
- 实际上,正如我们上面所说,如果一个类没有写virtual函数,那么这个类的作者就不想让它作为用于多态的基类。
- 如果你执意这样做,将该类(比如STL中的string,vector等)作为多态的Base类,那么结果往往是令人气急败坏的。
有时候纯虚函数是个好主意
- 我们需要在多态的Base中写一个virtual析构函数。而Base类需要被继承。
- 那么纯虚函数可能是一个好主意。纯虚函数所在的类自己无法创建对象,必须被继承。抽象类天生就是为了被继承。而且抽象类需要一个纯虚函数,那么不妨将析构函数写成纯虚函数。
小细节:
- 纯虚函数一般是不用实现的。但是这里的纯虚析构函数需要给出实现(一般是空函数),因为编译器在析构时,先调用Derive的析构函数,之后会产生一条调用Base析构函数的语句,如果你不给出实现,那么编译器就会生气。
class Base{
public:
Base(){
cout << "Base" << endl;
}
virtual ~Base() = 0 {
//用来 骗过 编译器
}
};
class Derive : public Base{
public:
Derive(){
cout << "Derive" << endl;
}
~Derive() {
cout << "Derive" << endl;
}
};
int main() {
Base* bp = new Derive();
delete bp;
return 0;
}
- 某些类不是作为Base使用的,如string,那么就更不用说多态了。
- 有些类即使用于Base,也不是用来提供多态功能,那么这种类也不需要virtual析构函数。
- 只有用于多态的Base类才需要virtual析构函数。
本文主要来自:《EffectiveC++》
(全文完)