@Author:CSU张扬
@Email:csuzhangyang@gmail.com or csuzhangyang@qq.com
@我的网站: https://www.cppbug.com
条款07-为多态基类声明virtual析构函数
给基类一个虚析构函数
许多做法可以记录时间,下面是一个计时的例子。
class TimeKeeper {
public:
TimeKeeper();
~TimeKeeper();
... ...
};
class AtomicClock : public TimeKeeper { ... }; // 原子钟
class WaterClock : public TimeKeeper { ... }; // 水钟
class WristWatch : public TimeKeeper { ... }; // 腕表
我们设计一个 工厂函数,返回一个基类指针,指向一个派生类对象。
TimeKeeper* getTimeKeeper();,同时 getTimeKeeper() 函数返回的对象必须位于 堆 上,这意味着我们要手动 delete 它。
上述代码引起的问题是,基类有一个 non-virtual 析构函数,假设我们的工厂函数 getTimeKeeper 返回了一个 AtomicClock 对象。这时我们 delete 一个指向派生类对象的基类指针是未定义行为(c++Primer(5th) P522)。我们可能只销毁了基类成员,而派生类的成员没有被销毁,造成一个 局部销毁 的对象。
最好的做法就是,给基类一个 虚析构函数。
不作为基类,则析构函数非虚
class Point {
public:
Point(int x, int y);
~Point();
private:
int x, y;
}
这个类总共大小是 8字节,如果我们的虚函数是 virtual 的,那么就要携带一份vptr(虚函数指针),这份指针大小为 4字节。那么多添加一个虚函数,就多占用了 50% 的空间。
若类的析构函数非虚,则不能作为基类被继承
例如,我们继承标准库中的 string ,它不含任何虚函数。
class SpecialString : public std::string {
... ...
};
SpecialString* pss = new SpecialString("Impending Doom");
std::string * ps = pss;
delete pa; // 造成一个 局部销毁 的对象。
注意: STL 所有容器都是不带 虚析构函数的类。
抽象类作为基类
含有纯虚函数的类,被称作为抽象类。
有时你想拥有一个抽象类,但是没有任何纯虚函数。这时就可以把析构函数声明为纯虚函数。
class AWOV {
public:
virtual ~AWOV() = 0;
};
AMOV::~AWOV() { }
AWOV 类有一个纯虚函数,所以是抽象类;它还有一个虚析构函数,所以被继承后的析构也没有问题。这里,我们必须为纯虚析构函数提供一份定义。
析构函数的运作方式是,最深层的那个派生类的析构函数最先被调用,然后是其基类的析构函数。编译器会在 AWOV 的派生类的析构函数中,调用 ~AWOV()。
总结
- 带多态性质的基类应该声明一个虚析构函数。如果一个类有任何一个虚函数,它就应该拥有一个虚析构函数。
- 某个类设计的目的如果不是为了作为基类来使用,或者不是为了具备多态性,就不该声明 虚析构函数。
- 某个类的析构函数非虚,则不能被用来做基类。
本文深入探讨C++中多态基类为何需要声明虚析构函数,解析虚析构函数在继承体系中的作用,避免派生类资源未正确释放导致的问题。并介绍了抽象类作为基类时的析构函数设计策略。
656

被折叠的 条评论
为什么被折叠?



