類的基本思想是數據抽象和封裝,數據抽象是一種依賴於接口和實現分離的編程技術。
定義在類內部的函數是隱式的 inline
函數。
成員函數必須在類的內部聲明,但是函數體可以在類內也可以在類外定義。
this
成員函數通過 this
指針來訪問調用它的那個對象,當調用一個成員函數時,用請求該函數的對象地址初始化 this
。
this
形參是隱式定義的,任何自定義名為 this
的參數或變量的行為都是違法的。
const 成員函數
C++ 允許把 const
關鍵字放在成員函數的參數列表之后,此時,緊跟在參數列表后面的 const
表示 this
是一個指向常量的指針,像這樣使用 const
的成員函數被稱為常量成員函數。
常量成員函數不能改變調用它的對象的內容。
類作用域和成員函數
編譯器首先編譯類成員的聲明,之后才輪到成員函數體,所以成員函數體可以隨意訪問類中的其它成員而無須在意這些成員的出現順序。
在類的外部定義成員函數
如果成員函數被聲明成常量成員函數,那么它的定義必須在參數初始化列表之后明確指定const屬性。
類外部定義的成員的名字必須包含它所屬類的名字。
定義類相關的非成員函數
如果函數在概念上屬於類但是不定義在類中,則一般應該與類聲明的頭文件在一個文件內,這種方式下,使用接口的任何部分都只需要引入一個文件。
構造函數
每個類都分別定義了它的對象被初始化的方式,類通過一個或幾個特殊的成員函數來控制其對象的初始化過程,這些函數稱為構造函數。
- 構造函數的任務是初始化類對象的數據成員,無論何時只要類的對象被創建,就會執行構造函數。
- 構造函數的名字與類名相同,構造函數沒有返回類型。
- 一個類可以有多個構造函數。
合成的默認構造函數
類通過一個特殊的構造函數來控制默認初始化過程,這個函數叫作默認構造函數,默認構造函數無須任何實參。
如果類沒有顯示地定義構造函數,那么編譯器就會為類提供一個隱式定義的默認構造函數。這個默認的構造函數又被稱為合成的默認構造函數。這個合成的默認構造函數將按照如下規則初始化類的數據成員:
- 如果存在類內的初始值,用它來初始化成員。
- 否則,默認初始化該成員。
合成的默認構造函數只適合非常簡單的類,對於一個普通的類來說,必須定義它自己的默認構造函數:
- 只有當類沒有聲明任何構造函數時,編譯器才會自動地生成默認構造函數。
- 如果類內包含了內置類型或者復合類型,則只有當這些成員全部都被賦予了類內的初始值時,這個類才適合於使用合成的默認構造函數,否則用戶在創建類的對象時就可能得到未初始化的值。
- 有的編譯器不能為某些類合成默認的構造函數。比如類中包含了一個其他類類型的成員且這個成員的類型沒有默認構造函數,那么編譯器將無法初始化該成員。
= default 的含義
C++11 新標准中,如果需要使用默認的行為,那么可以通過在參數列表后面加上= default
來要求編譯器生成默認構造函數。
= default
既可以和聲明一起出現在類內部,也可以作為定義出現在類的外部。和其它函數一樣,如果 = default
在類的內部,則默認構造函數是內聯的;如果它在類的外部,則該成員默認情況下不是內聯的。
構造函數初始值列表
Sales_data(const std::string &s,unsigned n,double p):
bookNo(s),units_sold(n),revenue(p*n)
構造函數初始值列表負責為新創建的對象的一個或幾個數據成員賦值。
構造函數不應該輕易覆蓋掉類內的初始值,除非新賦的值與原值不同。
如果不能使用類內初始值,則所有構造函數都應該顯示地初始化每個內置類型的成員。
拷貝、賦值和析構
除了定義類的對象如何初始化之外,類還需要控制拷貝、賦值和銷毀對象時發生的行為。
- 初始化變量以及以值的方式傳遞或返回一個對象時將發生拷貝。
- 當使用賦值運算符時會發生對象的賦值操作。
- 當對象不存在時執行銷毀操作。
如果不主動定義這些操作,編譯器會默認合成一個版本,編譯器生成的版本將對對象的每個成員執行拷貝、賦值和銷毀操作。
某些類不能依賴於合成的版本
當類需要分配類對象以外的資源時,合成版本的操作將會失效。
許多需要動態內存的類可以也應該使用 vector
對象或者 string
對象來管理必要的存儲空間,使用 vector
或string
的類能避免分配和釋放內存帶來的復雜性。
如果類包含 vector
或者 string
成員,則其拷貝、賦值和銷毀的合成版本能夠正常工作。