1. 程式人生 > >C++中設計類時的注意事項與遵循原則

C++中設計類時的注意事項與遵循原則

      首先要說的是預設建構函式,編譯器可以幫使用者定義一個預設建構函式,前提是使用者沒有定義任何建構函式,一旦使用者定義了某個建構函式,不管它是不是預設的,那麼編譯器都不會再幫使用者定義預設構造函數了,在使用者定義自己的預設建構函式時,要麼沒有引數,要麼所有的引數都有一個預設值【比如有一個類A,那麼帶引數的預設建構函式應該是這樣 A(int a=0,string s = "null")】。接著考慮在繼承鏈中的類,如果有一個類B,它繼承自類A(或者說B裡面有一個類A的成員元件),那麼對於B的建構函式而言,如果裡面沒有顯式的呼叫類A的建構函式(不管是不是預設建構函式),那麼編譯器會自動呼叫類A的預設建構函式(如果類A沒有預設建構函式,那麼會發生編譯錯誤!),以便初始化B中屬於類A的資料成員。總而言之,為一個類定義預設建構函式是一種良好的程式設計習慣,它可以確保類的資料成員會被初始化為正確的值。

      其次要注意的是複製建構函式和賦值操作符過載函式,先說複製建構函式,所謂複製建構函式,就是說這個建構函式的引數表裡面的引數是本類型別的類物件。複製建構函式主要應用在以下場景:

1、用這個類物件初始化一個新的同類型別物件
2、將這個類物件作為一個引數傳遞給某個函式(以按值傳遞的方式)
3、某個函式需要返回這個類的物件
4、在某些場合下需要建立這個類的臨時物件

同預設建構函式一樣,如果使用者沒有定義自己的複製建構函式,那麼編譯器將會生成一個複製建構函式,但這種編譯器做出來的複製建構函式是有著致命缺陷的:編譯器的複製建構函式只是簡單的對資料成員進行淺拷貝,可如果類中的資料成員有動態記憶體(比如指標)的話,那麼這種淺拷貝就會造成錯誤。所以遇到這種情況時,使用者應該自己定義一個深度拷貝的複製建構函式。

      同複製建構函式一樣,賦值操作符過載函式也應該由使用者自己定義,需要注意的是,賦值操作符是不能被繼承的,這是因為子類繼承的函式方法,其引數表與父類對應的那個函式方法完全一致,但對於賦值操作符函式來說就不一定了,因為很常見的一個情況是:同類物件之間的賦值操作,它們的函式引數都是本類型別,如果子類能繼承父類的賦值操作符函式,那麼子類的一些新增資料成員,有可能就無法得到正確的處理了。但是如果我們將一個子類的物件賦值給一個父類物件時(沒有為子類定義賦值操作符),那麼編譯器生成的陰式賦值操作符,將會把子類物件中的父類相關的東西剝離開來,然後用父類的賦值操作符函式來賦值,而對子類中的其他資料成員,都會採用簡單的拷貝方式賦值,那麼反過來將父類物件賦值給子類物件呢?比如B是A的子類,那麼【B物件 = A物件】呢?由於賦值操作會被編譯器轉換成=號左邊的那個型別的賦值操作符,所以實際呼叫的是B::operator(...),再由於編譯器不支援型別的向下轉換,所以這句程式碼是錯的,除非我們為A->B定義了一個轉換函式。同樣的道理,如果被賦值物件型別不同,那麼編譯器將使用被賦值物件的那個賦值操作符函式,並且最好有相應的型別轉換函式。

私有成員(private)與保護成員(protected)的區分

       對於子類而言,父類中的保護成員其實就是公有成員,但對於外部而言,保護成員則相當於私有成員。在子類中可以直接訪問父類的保護成員,但是父類的私有成員如果子類想訪問,那就只能通過父類的成員函式類訪問了。至於具體怎麼劃分這個界限,使用者需要直接酌情考慮。

公有方式派生的子類中的父類方式的說明:

1、子類物件可以自動使用父類的方法

2、子類的建構函式會自動呼叫父類的建構函式,如果沒有在成員初始化列表中指出其它建構函式的話,那麼呼叫的會是父類的預設建構函式(不帶引數的那個)

3、在子類的函式中,可以使用父類的作用於解析操作符來顯式呼叫父類的受保護成員方法,但無法訪問私有成員方法

4、子類的友元函式可以通過強制型別轉換,將子類的引用或指標轉換為父類的引用或指標,然後通過這個引用或指標來呼叫父類的友元函式。