【GeekBand】C++面向對象高級編程-第三周筆記

課堂大綱:

1.組合與繼承
1.1 Composition 復合
1.2 Delegation 委托
1.3 Inheritance 繼承
2.虛函數與多態
2.1虛函數


正文

1.組合與繼承

1.1Composition 復合

復合表示has-a,表示一個類里含有另一個類的對象(非指針及引用)。
例如

    template<class T>
    class queue
    {
        ...
    protected:
        deque<T> c;   //底層容器
    };

其中c是該類的一個成員,是c這個對象及其成員變量是占據queue內存的。
UML表示方法是:

2016-05-26_082626.png

黑色實心菱形從queue類指向deque類,一個簡單的記憶方法是:菱形是實心的,表示這個queue是真的有這個deque的實體,然后這個菱形指向了deque類,表示這個菱形表示deque。
Composition關系下的構造和析構
構造:
構造是由內而外,就像打包裹,從里往外。先構造components的對象再構造container,這是編譯器自動完成的。

Container::Container(...):Component(){...};

析構:
析構是由外而內,就像拆包裹,從外往里。先析構Container再析構components的,這也是編譯器自動完成的。

Container::~Container(){~Component();}

1.2 Delegation委托

委托表示composition by reference,表示一個類里含有另一個類的指針或者引用對象。
例如:

class String
{
private:
    StringRep* rep; //pimml
};

string.JPG

黑色空心菱形從String類指向StringRep類,一個簡單的記憶方法是:菱形是空心的,表示這個String是只有這個StringRep的指針對象或者引用,然后這個菱形指向了StringRep類,表示這個菱形表示StringRep類。
著名的寫法:編譯防火墻
一個類A只提供接口,具體實現用另一個類B來完成,其中A與B的 關系是委托關系,好處是可以切換具體實現,不影響接口。
編譯防火墻.JPG

a b c共享數據,如果a要改數據,那么系統就會復制一份專門給a修改,而不會影響b和c 的使用。

1.3Inheritance 繼承

復合表示is-a
例如:

struct _List_node_base
{
    _List_node_base* _M_next;
    _List_node_base* _M_prev;
};
template<typename _Tp>
struct _List_node: public _List_node_base
{
    _Tp _M_data;
};

UML表示方式為:

繼承.JPG

圖中表示的方式與前面兩種略有不同,結合三角形,可以看成是有子類指向父類,這是一個泛化的過程,譬如老虎→動物。
其中繼承方式有public、protected、private三種,現簡單介紹一下其特性。

public protected private
公共繼承 public protected 不可見
私有繼承 private private 不可見
保護繼承 protected protected 不可見

在上圖中:1)基類成員對派生類都是:共有和保護的成員是可見的,私有的的成員是不可見的。
** 2)基類成員對派生類的對象來說:要看基類的成員在派生類中變成了什么類型的成員。如:私有繼承時,基類的共有成員和私有成員都變成了派生類中的私有成員,因此對于派生類中的對象來說基類的共有成員和私有成員就是不可見的。**

父類中的數據會被子類繼承下來,但是子類能否直接訪問這些數據需要根據上表的特性來考慮。
當繼承與虛函數搭配時,能充分發揮出繼承的價值。關于虛函數,下文會提到。
繼承關系下的構造和析構
構造:
繼承關系下的構造還是由內而外地,derived類先構造base類的,再構造自己的,這是編譯器自動完成的

Derived::Derived(...):Base(){...}

析構:
析構則是由外而內地,先析構derived的再析構base的,這也是編譯器自動完成

Derived::~Derived() {~Base();}

值得注意的是,《Effective C++》條款07中有提到

當derived class 對象經由一個base class指針被刪除,而該base class 帶有一個non-virtual 析構函數時,其結果未有定義——實際執行時通常發生的是對象的derived成分沒被銷毀。
消除這個問題的做法很簡單:給base class 一個virtual析構函數。此后刪除derive class對象就會如你想要的那般。

讀者可以簡單地寫一個類來測試一下,這里筆者就隨便寫一個例子來說明這個問題。

#include<iostream>
using namespace std;

class Base
{
public:
    Base( ) { cout << "I'm Base's Ctor" << endl; }
    ~Base( ) { cout << "I'm Base's Dtor" << endl;}
};

class Derived: public Base
{
public:
    Derived( ) { cout << " I'm Derived's Ctor"<<endl; }
    ~Derived( ){cout<<"I'm Derived's Dtor"<<endl;}
};

int main()
{
    Base *base = new Derived;
    delete base;
}

運行結果:


捕獲.JPG

可以清楚看到這就發生了上述內存泄露的問題了。為此
修改程序如下:

#include<iostream>
using namespace std;

class Base
{
public:
    Base( ) { cout << "I'm Base's Ctor" << endl; }
    virtual ~Base( ) { cout << "I'm Base's Dtor" << endl;}
};

class Derived: public Base
{
public:
    Derived( ) { cout << " I'm Derived's Ctor"<<endl; }
    ~Derived( ){cout<<"I'm Derived's Dtor"<<endl;}
};

int main()
{
    Base *base = new Derived;
    delete base;
}

運行結果如下:

捕獲1.JPG

所以在父類的析構函數中加了virtual關鍵字后,delete父類指針時可以還調用子類的析構函數,從而避免了內存泄露。
值得提醒一下的時,《Effective C++》07條款中給的提醒是:

無端地將所有classes的析構函數聲明為virtual,就像從未聲明它們virtual一樣,都是錯誤的。許多人的心得是:只有當class內含至少一個virtual函數才為它聲明virtual析構函數

好了,終于進入下一部分了。

2.虛函數與多態

2.1虛函數

首先簡單介紹三個關于虛函數的名詞:
non-virtual函數:非虛函數,這個函數是你不希望子類重新定義它。
virtual函數:虛函數,你希望子類重新定義,而且父類中已經定義過這個函數。
pure virtual函數: 純虛函數,你希望子類一定要重新定義且父類中并無默認定義。
例子如下:

class Shape
{
public:
    virtual void draw( ) const = 0; //純虛函數
    virtual void error( const std::string& msg);//虛函數
    int objectID( ) const;//非虛函數
    ...
};

class Rectangle: public Shape {...};
class Ellipse:public Shape{...};
```
**繼承+復合關系下的構造和析構**

![捕獲3.JPG](http://upload-images.jianshu.io/upload_images/2020078-f84f329c06b6ff91.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
三個類的關系如上圖所示,那么我們用簡單的程序測試一下:

```C++
#include<iostream>
using namespace std;
 
class Base
{
public:
  Base(){cout<<"Base ctor!"<<endl;}  
  ~Base(){cout<<"Base dtor!"<<endl;}
  int base;
};
 
class Component
{
public:
  Component(){cout<<"Component ctor!"<<endl;}  
  ~Component(){cout<<"Component dtor!"<<endl;}
  int component;
};
 
class Derived:public Base
{
public:
  Derived(){cout<<"Derived ctor!"<<endl;} 
  ~Derived(){cout<<"Derived dtor!"<<endl;}
  int derived;
  Component cpt;
};
 
int main()
{
  Derived d;
  return 0;
}
```
運行結果是:
```
Base ctor!
Component ctor!
Derived ctor!
Derived dtor!
Component dtor!
Base dtor!
```
那么如下圖的關系呢,我們再修改一下程序測試一下:
![捕獲4.JPG](http://upload-images.jianshu.io/upload_images/2020078-641806665254d409.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
```C++
#include<iostream>
using namespace std;
 
 
class Component
{
public:
  Component(){cout<<"Component ctor!"<<endl;}  
  ~Component(){cout<<"Component dtor!"<<endl;}
  int component;
};
 
class Base
{
public:
  Base(){cout<<"Base ctor!"<<endl;}  
  ~Base(){cout<<"Base dtor!"<<endl;}
  int base;
  Component cpt;
};
 
class Derived:public Base
{
public:
  Derived(){cout<<"Derived ctor!"<<endl;} 
  ~Derived(){cout<<"Derived dtor!"<<endl;}
  int derived;
};
 
int main()
{
  Derived d;
  return 0;
}
```
運行結果是:
```C++
Component ctor!
Base ctor!
Derived ctor!
Derived dtor!
Base dtor!
Component dtor!
```
和打包裹的例子是一致的,打包(構造)的時候是先包裝最里面的,然后再一層一層裝外面的。拆包(析構)都是從最外面的包裝開始拆起,直到拆到后面發現盒子里只剩下這么一點空氣了:)

**委托+繼承**
*待續...*

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容