# [22] 繼承 — 抽象基類(ABCs)
## FAQs in section [22]:
* [22.1] 將接口和實現分離的作用是什么?
* [22.2] 在C++中如何分離接口和實現(就象?Modula-2)?
* [22.3] 什么是?ABC?
* [22.4] 什么是“純虛”成員函數?
* [[22.5] 如何為包含指向(抽象)基類的指針的類定義拷貝構造函數或賦值操作符?
](abcs.html#[22.5])
## 22.1 將接口和實現分離的作用是什么?
接口是公司最有價值的資源。設計接口比用一堆類來實現這個接口更費時間。而且接口需要更昂貴的人力的時間。
既然接口如此有價值,它們應該被保護,以免因為數據結構和其他實現的改變而被破壞。因此,應該將接口和實現分離。
## 22.2 在C++中如何分離接口和實現(就象?Modula-2)?
使用ABC。(譯注:即抽象基類?abstract?base?class)
## 22.3 什么是?ABC?
抽象基類(abstract base class)。
在設計層次,抽象基類(ABC)對應于抽象概念。如果你問一個機修工他是否修理交通工具,他可能想知道你所說的是哪種交通工具。他不修理航天飛機、遠洋輪、自行車或核潛艇。問題在于“交通工具”是一個抽象概念(例如,除非你知道你要建造哪種交通工具,否則你無法建造一個“交通工具”)。在C++中,`Vehicle`(交通工具)類是一個ABC,而`Bicycle`(自行車),`SpaceShuttle`(航天飛機)等則是派生類(`OceanLiner(遠洋輪)`是一種`Vehicle`)。在真實世界的OO中,ABC無處不在。
在程序語言層次上,抽象基類(ABC)是有一個或多個純虛成員函數的類。無法建立抽象基類的對象(實例)。
## 22.4 什么是“純虛”成員函數?
將普通類變成抽象基類(也就是ABC)的成員函數。通常只在派生類中實現它。
某些成員函數只在概念中存在,而沒有合理的定義。例如,假設我讓你在坐標`(x,y)`處畫一個圖形,大小為?7。你會問我:“我應該畫哪種圖形?”(圓,矩形,六邊形等,畫法都不同)。在C++中,我們必須指出?`draw()`?成員函數的實在物(由此用戶才能在有一個`Shape*`或者?`Shape&`?的時候調用它),但我們認識到,在邏輯上,它只能在子類中被定義:
```
?class?Shape?{
?public:
???virtual?void?draw()?const?=?0;??//?=?0?表示它是?"純虛"?的
//?...
?};
```
這個純虛函數使?`Shape`?成為了ABC(抽象基類)。如果你愿意,你可以將“`=?0;`”語法看作為代碼位于NULL指針處。因此?`Shape`?向它的用戶承諾了一個服務,然而?`Shape`?無法提供任何代碼來實現這個承諾。這樣做使得即使基類沒有足夠的信息來實際定義成員函數時,也強制了任何由?`Shape`?派生的具體類的對象須給出成員函數。
注意,為純虛函數提供一個實現是可能的,但是這樣通常會使初學者糊涂,并且最好避免這樣,直到熟練之后。
## 22.5 如何為包含指向(抽象)基類的指針的類定義拷貝構造函數或賦值操作符?
如果類擁有被(抽象)基類指針指向的對象,則在(抽象)基類中使用虛構造函數用法](virtual-functions.html#[20.5])。就如同一般用法一樣,在基類中聲明一個[純虛方法?`clone()`?:
```
?class?Shape?{
?public:
???//?...
???virtual?Shape*?clone()?const?=?0;???//?虛擬(拷貝)構造函數
//?...
?};
```
然后在每個派生類中實現?`clone()`方法:
```
?class?Circle?:?public?Shape?{
?public:
???//?...
???virtual?Shape*?clone()?const?{?return?new?Circle(*this);?}
???//?...
?};
?class?Square?:?public?Shape?{
?public:
???//?...
???virtual?Shape*?clone()?const?{?return?new?Square(*this);?}
???//?...
?};
```
現在假設每個 `Fred` 對象有一個?`Shape`對象。`Fred`對象自然不知道?`Shape`是圓還是矩形還是……。`Fred`的拷貝構造函數和賦值操作符將調用`Shape`的`clone()`方法來拷貝對象:
```
?class?Fred?{
?public:
???Fred(Shape*?p)?:?p_(p)?{?assert(p?!=?NULL);?}???//?p?must?not?be?NULL
??~Fred()?{?delete?p_;?}
???Fred(const?Fred&?f)?:?p_(f.p_->clone())?{?}
???Fred&?operator=?(const?Fred&?f)
?????{
???????if?(this?!=?&f)?{??????????????//?檢查自賦值_
?????????Shape*?p2?=?f.p_->clone();???//?Create?the?new?one?FIRST...
?????????delete?p_;???????????????????//?...THEN?delete?the?old?one
?????????p_?=?p2;
???????}
???????return?*this;
?????}
???//?...
?private:
???Shape*?p_;
?};
```
- C++ FAQ Lite
- [1] 復制許可
- [2] 在線站點分發本文檔
- [3] C++-FAQ-Book 與 C++-FAQ-Lite
- [6] 綜述
- [7] 類和對象
- [8] 引用
- [9] 內聯函數
- [10] 構造函數
- [11] 析構函數
- [12] 賦值算符
- [13] 運算符重載
- [14] 友元
- [15] 通過 <iostream> 和 <cstdio>輸入/輸出
- [16] 自由存儲(Freestore)管理
- [17] 異常和錯誤處理
- [18] const正確性
- [19] 繼承 — 基礎
- [20] 繼承 — 虛函數
- [21] 繼承 — 適當的繼承和可置換性
- [22] 繼承 — 抽象基類(ABCs)
- [23] 繼承 — 你所不知道的
- [24] 繼承 — 私有繼承和保護繼承
- [27] 編碼規范
- [28] 學習OO/C++
- [31] 引用與值的語義
- [32] 如何混合C和C++編程
- [33] 成員函數指針
- [35] 模板 ?
- [36] 序列化與反序列化
- [37] 類庫