1 類的定義
1.1 類定義的格式
1 class為定義類的關鍵字,Stack為類的名字,{}中為類的主體,注意類定義結束時后面分號不能省 略》。類體中內容稱為類的成員:類中的變量稱為類的屬性或成員變量; 類中的函數稱為類的方法或者成員函數。2 為了區分成員變量,?般習慣上成員變量會加一個特殊標識,如成員變量前面或者后面加_ 或者m開頭,注意C++中這個并不是強制的,只是?些慣例,具體看公司的要求。3 C++中struct也可以定義類,C++兼容C中struct的用法,同時struct升級成了類,明顯的變化是struct中可以定義函數,一般情況下我們還是推薦用class定義類。4 定義在類里面的成員函數默認為inline。
?有了類我們就可以在實現棧等數據結構時就不需要加上“ST"這種前綴
1.2 訪問限定符
1 類是C++中?種實現封裝的方式,用類將對象的屬性與方法結合在?塊,讓對象更加完善,通過訪問權限選擇性的將其接口提供給外部的用戶使用。2 public修飾的成員在類外可以直接被訪問;protected和private修飾的成員在類外不能直接被訪問3 訪問權限作用域從該訪問限定符出現的位置開始直到下?個訪問限定符出現時為止,如果后面沒有訪問限定符,作用域就到 }即類結束。4 class定義成員沒有被訪問限定符修飾時默認為private,struct默認為public。5 一般成員變量都會被限制為private/protected,需要給別人使用的成員函數會放為public。
?1.3 類域
1 類定義了?個新的作用域,類的所有成員都在類的作用域中,在類體外定義成員時,需要使用?::?作用域操作符指明成員屬于哪個類域。?2 類域影響的是編譯的查找規則,下面程序中Init如果不指定類域Stack,那么編譯器就把Init當成全局函數,那么編譯時,找不到array等成員的聲明/定義在哪里,就會報錯。指定類域Stack,就是知道Init是成員函數,當前域找不到的array等成員,就會到類域中去查找。
#include<iostream>
using namespace std;
class Stack
{
public:
// 成員函數
void Init(int n = 4);private:
// 成員變量
int* array;
size_t capacity;
size_t top;
};
// 聲明和定義分離,需要指定類域
void Stack::Init(int n)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申請空間失敗");
return;
}
capacity = n;
top = 0;
}
int main()
{
Stack st;
st.Init();
return 0;
}
?其中size_t是無符號整數類型
2 實例化
2.1 實例化的概念
1 用類類型在物理內存中創建對象的過程,稱為類實例化出對象。2 類是對對象進行?種抽象描述,是?個模型?樣的東西,限定了類有哪些成員變量,這些成員變量只是聲明,沒有分配空間,用類實例化出對象時,才會分配空間。3 一個類可以實例化出多個對象,實例化出的對象 占用實際的物理空間,存儲類成員變量。打個比方:類實例化出對象就像現實中使用建筑設計圖建造出房子,類就像是設計圖,設計圖規劃了有多 少個房間,房間大小功能等,但是并沒有實體的建筑存在,也不能住人,用設計圖修建出房?,房 子才能住人。同樣類就像設計圖?樣,不能存儲數據,實例化出的對象分配物理內存存儲數據。
2.2 對象的大小?
分析?下類對象中哪些成員,類實例化出的每個對象,都有獨立的數據空間,所以對象中肯定包含成員變量那么成員函數是否包含呢?首先函數被編譯后是?段指令,對象中沒辦法存儲,這些指令存儲在?個單獨的區域(代碼段),那么對象中非要存儲的話,只能是成員函數的指針。那么對象中是否有存儲指針的必要呢,Date實例化d1和d2兩個對象,d1和d2都有各自獨立的成員變量存儲各自的數據,但是d1和d2的成員函數指針是一樣的,存儲在對象中就浪費了。如果用Date實例化100個對象,那么成員函數指針就重復存儲100次,太浪費了。所以函數指針是不需要存儲的。函數指針是一個地址(第一行指令的地址),調用函數被編譯成匯編指令[call 地址], 其實編譯器在編譯鏈接時,就要找到函數的地址,不是在運行時找,只有動態多態是在運行時找,就需要存儲函數地址上面我們分析了對象中只存儲成員變量,C++規定類實例化的對象也要符合內存對齊的規則。
綜上所述,計算一個對象的大小不需要加上成員函數的大小,同時也要遵循內存對齊的原則(高效訪問)來計算?
內存對齊的原則
1 第?個成員在與結構體偏移量為0的地址處。2 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。3 注意:對齊數 = 編譯器默認的一個對齊數與該成員大小的較小值。4 VS中默認的對齊數為85 結構體總大小為:最大對齊數(所有變量類型最大者與默認對齊參數取最小)的整數倍。6 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。
示例如下?
#include<iostream>
using namespace std;
// 計算?下A/B/C實例化的對象是多??
class A
{
public:
void Print()
{
cout << _ch << endl;
}
private:
char _ch;
int _i;
};
class B
{
public:
void Print()
{
//...
}
};
class C
{};
int main()
{
A a;
B b;
C c;
cout << sizeof(a) << endl;
cout << sizeof(b) << endl;
cout << sizeof(c) << endl;
return 0;
}
?輸出的結果因該為8? ? ? ? 1? ? ? ? 1,在A中_ch,變量和_i變量經過內存對齊,大小為8,B和C看似是0,但其實會留小大小為1的空間占位表示成員函數的存在
3 this指針
1 Date類中有 Init 與 Print 兩個成員函數,函數體中沒有關于不同對象的區分,那當d1調?Init和Print函數時,該函數是如何知道應該訪問的是d1對象還是d2對象呢?那么這里就要看到C++給了一個隱含的this指針解決這里的問題2 編譯器編譯后,類的成員函數默認都會在形參第?個位置,增加?個當前類類型的指針,叫做this 指針。比如Date類的Init的真實原型為, void Init(Date* const this, int year, int month, int day)3 類的成員函數中訪問成員變量,本質都是通過this指針訪問的,如Init函數中給_year賦值, this->_year = year;4 C++規定不能在實參和形參的位置顯示的寫this指針(編譯時編譯器會處理),但是可以在函數體內顯示使用this指針。
? ? ??