EffectiveC++详解:条款07-为多态基类声明virtual析构函数

文章目录

  • 条款07-为多态基类声明virtual析构函数
    • 给基类一个虚析构函数
    • 不作为基类,则析构函数非虚
    • 若类的析构函数非虚,则不能作为基类被继承
    • 抽象类作为基类
    • 总结

@Author:CSU张扬
@Email:[email protected] or [email protected]
@我的网站: 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()

总结

  • 带多态性质的基类应该声明一个虚析构函数。如果一个类有任何一个虚函数,它就应该拥有一个虚析构函数。
  • 某个类设计的目的如果不是为了作为基类来使用,或者不是为了具备多态性,就不该声明 虚析构函数。
  • 某个类的析构函数非虚,则不能被用来做基类。

你可能感兴趣的:(EffectiveC++详解)