天天看點

《C++面向對象高效程式設計(第2版)》——2.12 類中的通路區域

本節書摘來自異步社群出版社《c++面向對象高效程式設計(第2版)》一書中的第章,第2.12節,作者: 【美】kayshav dattatri,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

c++面向對象高效程式設計(第2版)

每個類都有3個不同的通路區域。在我使用過的所有oop語言中,隻有c++精心設計了這3個區域。

public區域是最重要的區域,為類的使用者指定了類的接口。任何客戶都可以通路public區域。

本書的源代碼樣式

(1)所有的類名都以t開始。類的行為與在語言中添加的新類型類似,是以使用字首t 1。但是,在表示實作這些類的檔案名時,去掉t。例如,包含tint接口的檔案名是int.h。

(2)所有的成員函數名,首字母大寫(如add()、flipslip()等)。

(3)所有的枚舉(enum)都以e開始,枚舉中的成員名以e開始。

(4)函數中的所有局部(自動)變量名,首字母小寫。

(5)所有的常量都用大寫字母和數字表示。例如:

const unsigned days_in_week = 7

(6)下劃線(_)用于命名常量(如上days_in_week所示)。

(7)頭檔案中的注釋置于聲明之前,簡短注釋可與代碼位于同一行。

(8)所有的資料成員(執行個體變量)都以下劃線(_)開始。

(9)全局變量和全局常量通常以g開始。

例如:

tint(int value);

tint(long mostsignificant, unsigned long leastsignificant);

tint(long value);

tint(short value);

tint();

tint(const tint&amp; arg);<code>`</code>

以上都是構造函數,它們的行為與重載函數(編譯器将它們作為函數名不同的重載函數來實作)類似。欲了解更多構造函數相關内容,請參閱第3章。

(2)析構函數(destructor):名稱與類名相同,且帶字首~的成員函數稱為析構函數。在c++中,其他類型(或聲明)名稱都不會有~。當某個類對象不再處于程式段的作用域内時,該函數負責清理工作。與構造函數不同,一個類隻能有一個析構函數。析構函數在無用單元收集(garbage collection)中非常有用。

從一個函數(或塊)中退出時,編譯器将自動銷毀在該函數(或塊)中建立的對象。但是,對象可能已經聚集了資源(動态記憶體、磁盤塊、網絡連接配接等),這些資源儲存在對象的資料成員中,由成員函數操控。由于對象被銷毀(退出函數)後不可再用,是以,必須釋放該對象儲存的資源。但是,編譯器并不知道這些資源(它們可能由動态配置設定),是以,對象必須負責釋放它們。為了幫助對象完成這些工作,在退出函數(或塊)時,所有在該函數(或塊)中靜态建立(即不使用new()操作符建立)的對象都将調用析構函數。析構函數将釋放對象儲存的所有資源。換言之,析構函數提供了一種機制,即對象在被銷毀前可自行處理掉自身儲存的資源。參見下面複制構造函數的例子。析構函數的相關内容将在第3章中詳細讨論。

構造函數和析構函數都是特殊的成員函數。在聲明中,它們無任何傳回值類型,這表明它們不能傳回任何值2。

(3)複制構造函數(copy constructor):這是一個特殊的構造函數,用于通過現有對象建立新對象,因而稱為複制構造函數。複制構造函數有獨特的函數原型(或簽名)3,很容易識别。當内置資料類型變量(如int和char)從一個函數按值傳遞至另一個函數時,由編譯器負責複制該變量,并将其副本傳遞給被調函數(called function)。當對象按值傳遞給一個函數時,該對象的副本必須像内置類型那樣被複制。然而,對象不是簡單變量,它們是由程式員實作的複雜實體。是以,編譯器在複制對象時需要幫助。邏輯上,應該由對象的實作者負責複制對象(内置類型的實作者是編譯器)。複制構造函數就提供了這樣的幫助。無論何時需要對象的副本,編譯器都會調用複制構造函數來複制對象。特别是,當類在它的實作中使用動态記憶體時,複制構造函數必不可少。如果類的實作者不提供複制構造函數,編譯器将會自動生成一個複制構造函數。至于這個生成的複制構造函數是否滿足類的要求,那完全是另一個問題,在此暫不作贅述。注意,複制構造函數是一個特殊語義的構造函數。我們在建立和初始化一個新對象(從無到有地建立)時調用普通構造函數,在通過現有對象建立一個新對象時才調用複制構造函數。這是複制構造函數與其他構造函數的主要差別。出現下列情況時,将調用複制構造函數:

tint a;

tint b(100);

// ...

a = b;   // 将b指派給a,a和b都已存在。<code>`</code>

注意,源對象是b,目的對象4是a。兩個對象都是程式員使用的現存對象。如果類的實作未提供指派操作符,在需要時編譯器會為類生成一個指派操作符。與複制構造函數的情況相同,編譯器生成的指派操作符是否滿足類對象的要求是另一個問題,在此暫不作贅述。編譯器生成的指派操作符相當簡單,通常用處不大。詳見第3章。

(5)其他函數都是類的普通成員函數,它們通過<code>tint</code>類的對象來操作。隻有類的現有對象才能使用該類的成員函數(更精确地說是非靜态成員函數)。

(6)操作符函數(<code>operator function</code>):tint是我們定義的新類型,我們希望提供一些操作符(如同+和-)讓該類型更有用。也就是說,應該可以像整數(<code>int</code>)那樣使用tint類對象。例如:

// tint類型的操作符

tint operator+(const tint&amp; operand1, const tint&amp; operand2);

tint operator-(const tint&amp; operand1, const tint&amp; operand2);

// 諸如此類... 已略去<code>`</code>

為盡可能簡化示例,略去若幹細節。欲了解友元函數的概念,詳見第7章。

(7)成員函數聲明中const的意義:在以上示例的頭檔案中,許多函數後都綴有const關鍵字。例如:

<code>void print() const;</code>

這個const應用于函數,而非任何參數。這樣的函數稱為const成員函數,隻有成員函數(非普通函數)可以聲明為<code>const</code>。類的資料成員、函數的參數、對象、普通變量等也都可以聲明為<code>const</code>。<code>const</code>成員函數保證在被調用期間,不會改變調用對象的狀态。該例中,<code>print()</code>函數確定在被調用期間,不會修改調用對象的資料成員。這樣的函數稱為選擇器(selector),它隻能從對象中讀取資料,不能在對象的資料成員中寫入(修改)資料。是以,

typedef short errorcode;

class tlaserdiscplayer {

    public:

     // 操作

     errorcode play(unsigned atchapter=0);

     errorcode  stop(void);

     errorcode searchfor(unsigned chapter);

     errorcode opentray();

     errorcode closetray();

     void  poweron();

     void  poweroff();

     errorcode pause();

     // 構造函數

     tlaserdiscplayer();

     // 析構函數

     ~tlaserdiscplayer();

     // 略去其他函數

    private:

     enum etraystatus { eclosed, eopen };

     enum epowerstatus { eoff, eon };

     enum eplayermode { eplay, esearch, epause, estop };

     etraystatus  _traystatus;  // 打開或關閉

     epowerstatus _powerstatus; // 開機或關機

     eplayermode  _playermode;  // 播放、查找等

     // 略去其他成員

};<code>`</code>

以上的類聲明未揭示任何關于如何實作接口函數(或簡稱接口)的細節。在c++中,這樣的類聲明稱為接口檔案(interface file)或類頭檔案(class header file),常字尾.h擴充名。是以,以上示例的檔案表示為int.h。

繼續閱讀