C Primer 物件導向程式設計

2021-06-18 07:49:42 字數 4804 閱讀 5814

1.要注意區分類和類物件,物件只能訪問public成員,而類和友元可以訪問private成員。而對於基類的protected成員,派生類可以訪問其基類的protected成員(友元中也可以訪問protected成員),不能通過派生類物件在類外訪問protected成員(這點與private類似)(值得注意的是:在派生類中定義乙個函式接受派生類物件的引用(指標)或者直接定義乙個派生類物件《前向宣告只限制定義類的成員時只能定義為指標》,此時可以通過此引用(指標)或物件來訪問其基類的protected成員和派生類的private成員,原因在於此函式在類中

)。---》所以可以重新定義結論:pubic成員在類外和類中皆可訪問,protected成員只能在派生類類中訪問,private只能在類中訪問(友元可以訪問類的private和protected成員,當然也是在友元中--比如某個友元類的成員函式中)。

2.儘管不是必須這樣做,派生類一般會重定義所繼承的虛函式,如果沒有重定義則使用基類中定義的版本。派生類中虛函式的宣告必須與基類中的完全一致,但有乙個例外,基類中返回對基型別的引用(指標)的虛函式。派生類中的虛函式可以返回對基類或派生類的引用(指標)。一旦函式在基類中宣告為虛函式,則一直為虛函式,派生類無法改變該函式為虛函式這一事實,派生類重定義虛函式時,virtual可用可不用(只能在類定義體中使用)。

3.多型的實質是動態繫結,要觸發動態繫結必須滿足兩個條件:一,只有虛函式的呼叫才能進行動態繫結(如果呼叫非虛函式,無論物件是什麼型別,都執行基類型別定義的函式,前提是通過基類的引用或指標來呼叫)。二,必須通過對基類型別的引用或指標進行函式呼叫(物件是非多型的,呼叫的函式,無論是虛函式或非虛函式,是由物件的型別定義。只有通過引用或指標呼叫,虛函式才在執行時確定)。

4.公用,私有和受保護的繼承:

公用繼承(public),基類成員保持自己的訪問級別,基類的public成員為派生類的public成員,基類的protected成員為派生類的protected成員。

受保護繼承(protected),基類的public和protected成員在派生類中為protected成員。(注意基類中的public成員雖然在派生類中為protected但是也可以訪問,猜測此時視派生類為其派生類)

私有繼承(private),基類所有的成員在派生類中為private成員。

注意:派生類無法訪問基類的private成員,無論是什麼繼承都不能訪問。使用class保留字定義的派生類預設private繼承(沒有宣告是什麼繼承時),而struct定義的派生類為public繼承,class base;

struct d1:base{};public繼承

class d2:base{};private繼承

5.派生類恢復繼承成員的訪問級別,即在基類中的訪問級別,使用using來實現:

class base

protected:

std::size_t n; }

class derived : private base;

這樣定義之後,size成員在derived訪問級別從原來的private恢復為public,n在derived中訪問級別從private恢復為protected。

6.友元關係不能繼承,基類的友元對派生類的成員沒有特殊訪問許可權。如果基類定義了static成員,整個繼承層次中只有這樣乙個成員,無論基類派生多少個派生類,每個static成員只有乙個例項,並且static遵循訪問控制,如果在基類中為private則派生類不能訪問。

7.因為派生類包含乙個基類部分,意味著可以像使用基類物件一樣在派生類物件上執行操作,即派生類物件也是基類物件,存在從派生類引用(指標)到基類引用(指標)的自動轉換。而反過來,乙個基類物件可能只是派生類物件的一部分,結果,沒有從基類引用(指標)到派生類引用(指標)的(自動)轉換。相對於指標或引用,物件轉換的情況更為複雜,雖然一般可以使用派生型別的物件對基類型別的物件進行初始化或賦值,但是沒有從派生類型別物件到基類型別物件的直接轉換(即物件不能轉換,只能賦值或初始化)。-----比如將派生類引用傳遞給乙個接受基類型別引用的函式,引用直接繫結到該物件,物件本身並未複製,仍然是派生類物件。而如果函式接受的是基類型別物件,那麼該派生類物件的基類部分被複製到形參,即先轉換為基類物件,然後複製初始化。

注意 :只有在public繼承時,派生類(包括物件,指標,引用)才能轉換為基類(物件,指標,引用)--ps:書上說protected繼承時,後續派生類可以轉換為基類,但是這點自己測試時還沒有通過??

當派生類轉換為基類後,則按照基類的視角來訪問其成員(不能再訪問派生類的成員了,在基類中可以訪問在派生類中無法訪問的基類的private成員)。

8.從基類到派生類的自動轉換是不存在的,原因在於基類只能是基類,不能包含派生類的成員。編譯器甚至限制當基類指標或引用繫結到派生類物件後,從基類到派生類的轉換也是被限制的,理論上是安全的,但是編譯器只是根據型別來判斷有誤。

bulk_item bulk;

item_base * itemp = &bulk;

bulk_item  *bulkp  = itemp;//error,

不能將派生類轉換為基類後,再將基類轉換為派生類,即使我們知道這種轉換是安全的。但是在轉換安全的前提下,我們也可以使用static_cast強制編譯器進行轉換,或者可以使用dynamic_cast申請在執行時檢查。

9.建構函式和複製控制成員不能繼承(自己覺得書上這樣說是有問題,個人認為不是不能繼承,只是基類的建構函式和複製成員操作的物件是基類而不是派生類,派生類在例項化時首先呼叫基類的建構函式初始化基類部分,如果初始化列表為空則呼叫基類預設建構函式,當然也可以通過初始化列表傳參給基類的建構函式),每個類必須定義自己的建構函式和複製成員,不定義的話就使用合成版本。

10.如果類不包含指標這樣的成員,那麼複製控制可以使用合成的版本。但是如果包含指標則應定義自己的複製控制。複製建構函式合成版本中:派生類物件複製時,先呼叫合成的基類複製建構函式,再複製派生部分。賦值操作符和析構函式類似處理。但是如果派生類定義了自己的複製建構函式時,該複製建構函式一般應顯式的使用基類複製建構函式初始化基類部分,與建構函式類似,如果複製建構函式沒有向基類傳遞引數(此時的引數應為派生類物件)則呼叫的是基類的預設建構函式。

賦值操作符通常與複製建構函式類似,如果派生類定義了自己的複製操作符,則必須對基類部分進行顯式賦值。如下:

//base::operator==(const base &)

derived &derived ::operator=(constderived  & rhs)

return *this; }

析構函式與複製建構函式,賦值操作符不同。派生類析構函式不負責撤銷基類部分的成員,編譯器總是顯示的呼叫基類的析構函式,每個析構函式只負責清除自己的成員。物件的撤銷順序與構造順序相反,先執行派生類析構函式,在按照繼承層次依次向上呼叫基類析構函式(編譯器顯示呼叫)。

注意:刪除基類指標則執行基類析構函式並清除基類成員,但是如果物件實際是派生類物件,則沒有定義該行為,所以為了保證執行適當的析構函式,基類中的析構函式必須為虛函式,這樣一來,通過指標呼叫時,執行哪個析構函式將由指標所指的物件型別決定。即使析構函式沒有什麼工作要做,繼承層此的根類應定義析構函式為虛函式,由於虛函式的性質將被繼承,所以派生類及後續派生類的析構函式也將是虛函式。

11.建構函式和析構函式中的虛函式:派生類物件例項化時首先執行基類建構函式初始化物件基類部分,在執行基類建構函式時,物件派生類部分是沒有初始化的,此時物件還是不是乙個派生類物件。而撤銷派生類物件時,先執行派生類的析構函式,然後按照繼承層次逆序呼叫基類的析構函式。在這兩種情況下,物件都是不完整的,所以編譯器將物件的型別視為在構造或析構期間發生了變化。在基類建構函式或析構函式中,將派生類物件當作基類物件對待。如果不這樣的話,在基類建構函式或析構函式中呼叫虛函式時,則呼叫的是派生類版本從而可能訪問派生類物件成員,而此時物件的派生類部分成員還沒有初始化,這樣的訪問可能會造成程式崩潰。

12.繼承情況下的類作用域:

在繼承情況下,派生類的作用域巢狀在基類的作用域中。派生類作用域相當於區域性作用域而基類作用域相當於全域性作用域,通過派生類型別的指標或引用訪問成員時,首先在派生類中查詢,找不到的情況才在基類中查詢(對於函式一旦找到了名字,編譯器就不再繼續查詢了(當然如果有多個函式,還是會進行引數匹配),此時如果實參與形參不匹配,呼叫就會出錯)。而通過基類型別的指標或引用訪問時,將在基類中查詢而忽略派生類。與基類同名的派生類成員將遮蔽對基類成員的直接訪問(對於函式而言,只要函式同名即使函式原型不同,基類成員也會被遮蔽--類似區域性作用域中函式也會遮蔽全域性作用域中同名函式,即使函式原型不同),當然也可以使用作用域操作符訪問被遮蔽的基類成員。例如: base::mem。

類的多型性是屬於c++遮蔽機制中的一種特例,通過基類型別的引用或指標呼叫函式,理論上是只在基類中查詢而忽略派生類,但是由於函式是虛函式再加上通過基類型別的引用或指標呼叫從而觸發了動態繫結,所以才有可能呼叫到派生類中的同名函式(注意此時虛函式在基類和派生類中必須是同一原型(返回派生型別的物件等例外),這樣才符合多多型的定義,即同樣的**呼叫不同的函式)。

13.繼承與過載:

像其他函式一樣,成員函式(無論是虛還是非虛)也可以過載,由於遮蔽機制,如果派生類通過自身型別使用所有的過載版本,那麼派生類必須要麼重定義所有的過載版本,要麼乙個也不定義。但是有時候,類需要僅僅重定義乙個過載版本,並且想能使用所繼承的其他版本,顯然重定義所有基類版本不是乙個好選擇,此時可以引入using宣告。using宣告可以恢復基類成員的訪問級別(但是不能比原來的嚴格或寬鬆,即原來如果在public成員,則只能在派生類的public標號下使用using宣告),從將其作用域加入派生類中。同時由於using只能指定乙個名字,不能指定形參表,因此宣告的函式所有版本都會加到派生類的作用域中,這樣的話派生類只需要重定義本型別需要的版本函式名,其他版本可以使用從基類所繼承的。

14.含有(或繼承)乙個或多個純虛函式的類是抽象基類,不能建立抽象基類的物件。從抽象基類派生出的類必須實現全部的純虛函式,這樣就可以建立相應的物件了。否則還是抽象基類,不能建立物件。

15.覆蓋虛函式機制:想呼叫基類的虛函式版本,可以使用作用域操作符,base::fun();

c primer 物件導向程式設計

1.重構現象 因為派生類的建構函式只能初始化它的直接基類,所以這樣就出現了一種新的現象 重構,重構是很常見的,它是指在子類中重新定義父類的建構函式,已達到自己想要的建構函式。重構要注意的是一旦被重構,編譯器必須重新編譯這些類的 class pp class d1 public base class ...

C Primer 物件導向程式設計

1 在c 中,基類將型別相關的函式與派生類不做改變直接繼承的函式區分對待。對於某些函式,基類希望它的派生類各自定義適合自身的版本,此時基類就將這些函式宣告成虛函式 virtual function 2 在c 中,當我們使用基類的引用 或指標 呼叫乙個虛函式時將發生動態繫結。3 關鍵字virtual只...

c primer 物件導向程式設計筆記

1 動態繫結 在c 中,通過基類的引用 或指標 呼叫虛函式時,發生動態繫結。引用 或指標 既可以指向基類物件也可以指向派生類物件。2 虛函式 1 保留字virtual 只在類內部的成員函式宣告中出現,不能用在類定義體外部初相的函式定義上。2 派生類中虛函式的宣告必須與基類中的定義完全匹配,但有乙個例...