繼承:將一群相關的類組織起來,並讓我們得以分享期間的共通資料和操作行為; 父類:基類; 子類:派生類。
多型:可以使我們操縱不同類時,如同操縱單一個體。 讓基類的pointer 和 reference 得以十分透明的指向任何乙個派生類的物件。
動態繫結:解析操作會延遲到執行時才進行。
靜態繫結:程式執行之前就已經解析出應該呼叫哪乙個函式。
虛函式:virtual 實現了多型的機制。基類定義虛函式,子類可以重寫該函式;在派生類中對基類定義的虛函式進行重寫時,需要在派生類中宣告該方法為虛方法。
當子類重新定義了父類的虛函式後,當父類的指標指向子類物件的位址時,[即b b; a a = &b;] 父類指標根據賦給它的不同子類指標,動態的呼叫子類的該函式,而不是父類的函式,
且這樣的函式呼叫發生在執行階段,而不是發生在編譯階段,稱為動態聯編。而函式的過載可以認為是多型,只不過是靜態的。
同時在函式構造時,會依次呼叫父類、子類、子子類的呼叫函式;析構的順序則相反。 使用虛函式後,子類實現父類的虛函式後,呼叫該函式就會呼叫子類的函式而不是父類的函式
所有數列型別名稱被放在乙個名為ns_type的列舉型別中
class
num_sequence
;}
nstype() 函式會檢測整數引數是否返回某一有效數列。
class
num_sequence
};
static_cat是個特殊轉換記號,可將num轉換為對應的ns_type
不帶繼承的多型的缺點:費工夫,且事後維護工程浩大
定義抽象類的第乙個步驟:找出所有子類共通操作行為。就是定義基類的公有介面。
class
num_sequence
class
num_sequence
第三步:找出每個操作行為的訪問層次。皆可訪問:public 除基類外不需要用到:private 可以被派生類用到,但不允許一般程式使用
class
num_sequence
;virtual
intelem
(int pos)
const=0
;virtual
const
char
*what_am_i()
const=0
;//不確定是否有關 與型別
virtual ostream&
print
(ostream &os = cout) count =0;
static
intmax_elems()
;protected
:virtual
void
gen_elems
(int pos)
const=0
;bool
check_integrity
(int pos)
;//檢測是否有效
const
static
int _max_elems =
1024
;}
每個虛函式,要麼得有定義,要麼設定為純虛函式。
任何類如果宣告乙個或多個純虛函式,那麼由於其介面的不完整性(純虛函式沒有函式定義),程式無法為其產生任何物件。這種類只能作為派生類的子物件使用,而且前提是這些派生類必須
為所有虛函式提供確切的定義。
為什麼要定義純虛函式?
純虛函式是為你的程式制定一種標準,即只要你繼承了我,就必須按照我的標準來,實現我所有的方法,否則你也是虛擬的。
根據規則,凡基類定義有乙個或讀多個虛函式,應該要將其destructor宣告為virtual。
class
num_sequence
;
派生類由兩部分組成:一是基類構成的子物件,由基類的non-static data member組成;二是派生類的部分。
派生類的名稱之後緊跟著冒號,關鍵字public,及基類的名稱。
#include
"num_sequence.h"
class
fibonacci
:public num_sequence
fibonacci必須為從基類繼承而來的每個純虛函式提供對應的實現,還必須宣告專屬的member。
派生類的虛函式必須精確吻合基類中的函式原型,在類之外對虛函式進行定義時,不必指明關鍵字virtual。
每當派生類有某個member與其基類的member同名,便會遮掩注基類的那份member。也就是說,派生類內對該名稱的任何使用,都會被解析為該派生類自身的那份member,而非繼承來的那份member。
該問題的產生是由於基類中該函式 被視為虛函式。
data member如果是個reference,必須在constructor的member initialize list中加以初始化。一旦初始化,就再也無法指向另乙個物件。如果data member是pointer,就可以在construction內加以初始化,
也可以將它初始化為null,稍後再令它指向某個有效的記憶體位址。
初始化操作可以留給每個派生類,但這麼做會有潛在的危機。
較好的設計方式是,為基類提供constructor,並利用這個constructor處理基類所宣告的所有data member的初始化操作。
如果我們繼承了純虛函式,那麼這個派生類也會被視為抽象類,也就無法為它定義任何物件。
為什麼要定義純虛函式?
純虛函式是為你的程式制定一種標準,即只要你繼承了我,就必須按照我的標準來,實現我所有的方法,否則你也是虛擬的。
如果我們決定覆蓋基類所提供的虛函式,那麼派生類提供的新定義,其函式原型必須完全符合基類所宣告的函式原型,包括:引數列表、返回型別、常量性(const-ness)。
「返回型別必須完全吻合」規則也有意外 – 當基類的虛函式返回某個基類形式時,
class
num_sequence
派生類中的同名函式便可以返回該基類所派生出來的型別
class
fibonacci
:public num_sequence
}
怎麼合理設計乙份 what_am_i()函式,使其實現其功能?
1、在每個子類都擁有乙份what_am_i()函式,都返回乙個足以代表該類的字串;
2、只在父類提供乙份what_am_i()函式,令各派生類通過繼承機制加以復用。
3、為num_sequence增加乙個string member,並令每乙個派生類的constructor都將自己的類名作為引數,傳給num_sequence的constructor。
4、利用typeid運算子。執行時型別鑑定機制的一部分,由程式語言支援。
#include
inline
const
char
* num_sequence::
what_am_i()
const
ps -
>
gen_elems(64
);
預期呼叫的是fibonacci的gen_elems。但編譯時卻會發生錯誤。
為了呼叫fibonacci所定義的gen_elems(),必須指示編譯器,將ps的型別轉換為fibonacci指標。static_cast 運算子可以擔起這項任務。
if
(typeid
(*ps)
==typeid
(fibonacci)
)
static_cast 也有潛在危險,因為編譯器無法確認我們所進行的轉換操作是否完全正確。
dynamic_cast 運算子就可以提供有條件的轉換:
if
(fibonacci *pf =
dynamic_cast
>
(ps)
) pf-
>gen_e;
ems(64)
;
重讀Essential C 讀書筆記2
重讀essential c 讀書筆記2 by sssa2000 7 25 2004 第二章 面向過程的程式設計風格 很久以前我也很困惑為什麼要有面向過程物件導向的程式設計風格,雖然現在已經有很深的體會。其實不管是什麼風格,只要能更好地解決問題就是好的風格。1 傳值和傳址 lippman 在說明這個問...
讀書筆記 5
解碼gdi物件控制代碼 今天都是在探索gdi內部的結構,在微軟的文件中並沒有系統的記載,雖然我覺得很有道理,但到底是不是這樣只有微軟自己知道了 1 先看一下gdi控制代碼的定義,如hpen是這樣定義的 如果strict已經被定義了 struct hpen typedef struct hpen hp...
Effective C 讀書筆記 5
條款5 了解c 默默編寫並呼叫哪些函式 預設的函式包括 建構函式 copy建構函式 copy賦值運算子和析構函式,所有這些函式都是public且inline。這些函式僅當被呼叫的時候才會被編譯器建立出來。除非這個class的base class自身宣告有virtual析構函式,則這個class的析構...