天天看點

C++ Primer 學習筆記_71_面向對象程式設計 --句柄類與繼承面向對象程式設計

引言:

C++中面向對象程式設計的一個頗具諷刺意味的地方是:不能使用對象支援面向對象程式設計,相反,必須使用指針或引用。

通過pointer和reference進行的調用在運作時根據它們所綁定對象的動态類型而确定。但是使用指針或引用會加重類使用者的負擔,像上一節的習題15.29:需要使用者顯式釋放動态申請的記憶體。

C++解決該問題的一個通用的技術是定義包裝類或句柄類。句柄類存儲和管理基類指針,指針所指向對象的類型可以變化,它既可以指向基類類型對象又可以指向派生類型對象。使用者通過句柄類通路繼承層次的操作。因為句柄類使用指針執行操作,虛成員的行為将在運作時根據句柄實際綁定的對象的類型而變化。是以,句柄的使用者可以獲得動态行為但無須操心指針的管理。

包裝了繼承層次的句柄有兩個重要的設計考慮因素:

1)像對任何儲存指針的類一樣,必須确定對複制控制做些什麼。包裝了繼承層次的句柄通常表現得像一個智能指針,或者像一個值。

2)句柄類決定句柄接口屏蔽還是不屏蔽繼承層次,如果不屏蔽繼承層次,用于必須了解和使用基本層次中的對象。

一、指針型句柄

我們将定義一個名為Sales_item的指針型句柄類,表示Item_base層次。Sales_item的使用者将像一個指針一樣使用它:使用者将Sales_item綁定到Item_base類型的對象并使用*和->操作符進行Item_base的操作:

但是,使用者不必管理句柄指向的對象,Sales_item類将完成這部分工作。當使用者通過Sales_item類對象調用函數時,将獲得多态行為。

1、定義句柄

Sales_item類有三個構造函數:預設構造函數、複制構造函數和接受Item_base對象的構造函數。第三個構造函數将複制 Item_base 對象,并保證: 隻要 Sales_item 對象存在副本就存在。當複制 Sales_item 對象或給 Sales_item 對象指派時,将複制指針而不是複制對象。像對其他指針型句柄類一樣,将用使用計數來管理副本。 

迄今為止,我們已經使用過的使用計數式類,都使用一個夥伴類來存儲指針 和相關的使用計數。這個例子将使用不同的設計,如圖所示。

C++ Primer 學習筆記_71_面向對象程式設計 --句柄類與繼承面向對象程式設計

Sales_item 類将有兩個資料成員,都是指針:一個指針将指向 Item_base 對象,而另一個 将指向使用計數。Item_base 指針可以指向 Item_base 對象也可以指向Item_base 派生類型的對象。通過指向使用計數,多個 Sales_item 對象可以共享同一計數器。

除了管理使用計數之外,Sales_item類還定義解引用操作符和箭頭操作符:

2、使用計數式複制控制

複制Sales_item對象包括複制兩個指針和将使用計數加1。析構函數将使用計數減1,如果計數減至0就撤銷指針。因為指派操作符需要完成同樣的工作,是以在一個名為decr_use的私有實用函數中實作析構函數的行為。

指派操作符:

除了複制控制成員以外,Sales_item定義的其他函數是操作函數operator*和operator->,使用者将通過這些操作符通路Item_base成員。因為這兩個操作符分别傳回指針和引用,是以通過這些操作符調用的函數将進行動态綁定。

我們隻定義了這些操作符的const版本,因為基礎Item_base層次中的成員都是 const成員。

3、構造函數

出了複制構造函數,我們的句柄有兩個構造函數:

1)将Item_base指針設定為0以指出該句柄并未關聯任何對象。構造函數在自由存儲區申請一個新的計數器并将它初始化為1。

2)我們希望句柄的使用者建立自己的對象,并在這些對象上關聯句柄。構造函數将配置設定适當類型的新對象并将形參複制到新配置設定的對象中,這樣,Sales_item類将擁有對象并能夠保證在關聯到該對象的最後一個Sales_item對象消失之前不會删除對象。

二、複制未知類型

要實作接受Item_base對象的構造函數,必須首先解決一個問題:我們不知道給予構造函數的對象的實際類型。我們不知道它是一個Item_base對象或者是一個Item_base派生類型的對象。句柄類經常需要在不知道對象的确切類型時配置設定書籍的新副本。

【提示】

解決該問題的通用方法是定義虛操作進行複制,我們将該操作命名為clone。

為了支援句柄類,需要從基類開始,在繼承層次的每個類型中增加clone,基類必須将該函數定義為虛函數:

每個類必須重定義該虛函數。因為函數的存在是為了生成類對象的新副本,是以定義傳回類型為類本身:

定義句柄構造函數

它調用形參的clone産生那個對象的(虛)副本:如果實參是Item_base對象,則運作Item_base的clone函數;如果實參是Bulk_item對象,則執行Bulk_item的clone函數。

繼續閱讀