旅游

2016/08/06 四明山地质公园一日游

Posted by WangXiaoDong on August 6, 2016

时间:2016年8月6日 天气:晴转小雨:sunny:->:umbrella:


Author:冬之晓:dizzy_face:
Email: 347916416@qq.com
MyAppearance: MyAppearance

    今天,公司的同事泉庆带我们游览余姚著名的风景区“四明山地质公园”。一早,他
就开着车来到我们住的地方,然后带我、桂龙和江威去超市买了好多东西。之后开了大
约3个小时的车,来到了目的地。
    这里风景非常美,我们从山上走到山下,又从山下走回山上。感受到了无限的乐
趣和自然的瑰丽!
    晚上,泉庆带我们到一个地方吃了一顿!
    今天,不仅让我感受到余姚人的淳朴,也让我尝到了余姚的美食也是那么令人回味无穷!

为了纪念今天的旅游,发个图片纪念一下: 四明山地质公园玩耍

条款07:为多态基类声明virtual析构函数

有许多种做法可以记录时间,因此,设计一个TimeKeeper base class和一些derived classes作为不同的计时方法,相当合情合理:

class TimeKeeper{
public:
   TimeKeeper();
   ~TimeKeeper();
   class AtomicClock: public TimeKeeper{...};     //原子钟
   class WaterClock: public TimeKeeper{...};      //水钟
   class WristWatch: public TimeKeeper{...};      //腕表
   ...
}

许多客户只想在程序中使用时间,不想操心如何实现细节,这是可以用工厂模式,设计一个factory函数,返回指向一个计时对象。Factory函数“返回一个base类指针”,指向新生成的derived class对象:

TimeKeeper* getTimeKeeper();
TimeKeeper* p = getTimeKeeper();
...
delete p;

如上所示,分配的指针进行完相关操作后delete很重要。
由于p是一个基类的指针,当进行析构时,会调用基类的析构函数,也就是说会把p所指对象的基类部分进行,但是p实际指向了一个派生类的对象,对于派生类的数据怎么办呢?此时会产生不明确行为,这是造成内存泄露的一个绝佳方式。

如果我们把基类的析构函数做成虚函数,则这个问题就可以解决了:

class TimeKeeper{
public :
     TimeKeeper();
     virtual ~TimeKeeper();
     ....
};

class AtomicClock : public TimeKeeper{...};

class WaterClock : public TimeKeeper{...};

class WristClock : public TimeKeeper{...};

再次进行析构时,由于析构函数是一个虚函数,所以在析构时,会在运行期来决定调用哪个类的析构函数,此时,经判断是派生类的析构函数,所以会调用派生类的析构函数,此时,可以把ptk所指向的对象全部析构。

结论:任何class只要带有virtual函数都几乎确定应该也有一个virtual析构函数。

如果一个class不含virtual函数,通常表示它并不意图被用做一个base class,即使被当作基类,也不希望用基类的指针来指向一个派生类的对象。当class不企图被当作base class,令其析构函数为virtual,往往不是一个好主意。如下类:

class Point{

public :

     Point(int xCoord, int yCoord) : x(xCoord),y(yCoord){}

     ~Point();

private:

     int x, y;

}

这样一个类的对象,会占用64个字节的内存,如果其析构函数是一个虚函数,则此类的对象,将会是96字节,这样,浪费了50%的空间。

所以许多人的心得是:只有当class内含至少一个virtual函数,才为它声明一个virtual析构函数。

对于继承任何析构函数不是virtual的类,如果通过指针使用多态机制,最后通过delete该指向derived类型的base类型指针销毁原derived对象,就会出现泄漏的问题。

所以,包括所有STL容器和任何有非虚析构函数的类,请不要继承它们!!(C++11里面有一个final关键字,可以禁止派生,这样就好许多了!以后定义有非虚析构函数的类,最好都加上final关键字!而且标记为final的类,编译器则根本不会生成虚表。这样的代码显然更有效率。所以新标准真好!!!)

小技巧:但你需要一个抽象类,但是你没有一个虚函数,这时根据“当你打算使用的多态的时候,base class一般应该有个virtual析构函数”,你可以使用纯虚析构函数

注意:这时必须为这个纯虚析构函数提供一个定义(普通纯虚函数不用提供定义)。因为析构函数的运作机理是最深处的基类先被调用,然后是其后的每一个基类的析构函数被调用

并非所有的基类都是为了多态,对于不打算使用多态的情况,基类不需要virtual析构函数。

请记住:

  • Polymorphic base class 应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。
  • Class的设计目的如果不是作为base class 使用,或不是为了具备多态性,就不应该声明virtual析构函数。