1. 類的定義和聲明
一旦類定義完成後,就沒有任何方式可以增加成員了。
在類內部定義的成員函數(函數實現體在類中),則會認爲是inline成員函數(該函數被調用時直接換成函數體)
- 不完全類型:聲明瞭該類,在定義該類之前,成爲不完全類型:
- 只有當類定義體完成之後才能定義類,所以類不能具有自身類型的數據成員(因爲該類定義還沒有完成,不知道存儲該類的對象所需的空間,無法完成初始化)
- 但類數據成員可以是指向自身類型的指針或引用,因爲指針或者引用僅僅是一個地址空間或者別名,所以分配空間與該類無關,所以可以有。
- 類的定義必須以分號結束,因爲類的定義之後可以接受對象定義列表。
- mutable關鍵字: 數據成員是可變的(在const成員函數內也可變)。
2. this指針
this指針和調用成員函數的對象綁定在一起;
this指針是一個const指針,即指針本身不能改變,但是指向的內容可以改變(易理解,this和對象關聯,對象本身在函數調用過程中不能改變,但是對象中的數據成員可以改變)
- const 成員函數不能改變其所操作的對象的數據成員,是隻讀函數。並且必須在聲明和定義中均出現,不然會有編譯錯誤。
- 由上知,const成員函數既不能改變this所指內容,也不能改變this地址。
3. 類作用域
在類作用域外(即分號結束),定義類的數據成員和函數定義均需要指明屬於哪個類(完全限定名)。
- 形參和函數體中可以不指明類直接用類的數據成員,因爲函數名字已經指明瞭屬於哪一個類;
- 函數返回類型在函數名之前,在遇到函數返回類型的時候不知道屬於哪個類,所以如果函數返回類型用到了類中定義的某一個類型,則需要指明完全限定名。
類定義分爲兩個階段:
- 編譯成員聲明;
- 只有在所有成員出現之後,編譯它們的定義本身。
名字查找
- 在改名字的塊(函數作用域)中查找;
- 在所屬類成員(類作用域)的聲明;
- 在外圍作用域查找(如全局變量)等等。
4. 構造函數
構造函數不能聲明爲const類型, 因爲const函數除mutable數據成員外其他數據成員是不可改變的,而構造函數就是給成員函數賦值的,兩者相悖,所以error。
構造函數過程:
- 初始化階段(初始化列表),也就是爲各個數據成員分配相應的空間;
- 普通計算階段(函數體中的操作,如給數據成員賦值等)。
由上可知,類數據成員初始化化完成之後纔會進入構造函數函數體中。
必須使用初始化列表的情況:
- 任何const成員數據,因爲const變量一旦確定不能改變,則必須在初始化時確定,在普通計算階段(構造函數體內)進行賦值操作時不可以的;
- 引用類型成員, 因爲引用是別名,則定義引用時必須初始化,在函數體中直接賦值時該引用必須是已經定義過得,所以引用也必須在初始化列表中;
- 沒有默認構造函數的類類型的成員,如果沒有爲類成員提供初始化式,則編譯器將會調用。
數據成員在初始化次序與其聲明順序有關,與在初始化列表中的順序無關。
如果類包含內置或複合類型的成員,則該類應該定義自己的構造函數來初始化這些成員。
- 抑制構造函數的隱式轉化,使用explicit關鍵字
class A{
A(string str);
bool isSame(A&);
}
string str = "str";
A a = new A("aaa");
a.isSame(str);//會隱式的將string類型轉化爲A對象,進行判斷。但是這個往往會造成誤區,所以用explicit關鍵字來說明參數不接受隱式轉化, explicit bool isSame(A&)
5. 友員
爲了實現一個類使用另一個類的私有成員。
例子:
class A{
friend class B; //B可以用A中的私有成員和函數,但B不是A的數據成員
friend int C::get(); // C的get方法可以用A中的私有成員和函數,但此函數不是A的成員函數
}
- 必須先定義包含成員函數的類,才能將成員類或者函數定義爲友元。即必須定義A,才能定義B和C。因爲B和C中依賴A中定義的成員。
- 但是它們的聲明沒有限制。
6. static類成員
- 與類有關,與類對象無關;
優點:
- 在類作用域中,避免與其他變量名衝突;
- 封裝,只與該類有關;
- 可以用於存儲和類對象無關的成員,意圖明確。如Thread類中的sleep()函數。
沒有this指針
- const static成員,可以在類的定義體重初始化,但是仍需在類外進行定義。普通成員不行。
- static數據成員的類型可以是該成員所屬的類型,因爲static成員獨立於任何對象而存在,不是類類型對象的組成部分。(非static必須是指針或引用)
class A{
static A a; //ok
}