C++類和對象(一)&&實現offsetof宏&&this指針

發布日期:2019-10-25

一.目錄

  1.對象的相關知識

  2.類的定義

  3.類的實例化

  4.類對象模型

  5.模擬實現offsetof宏

  6.this指針

二.正文

1.對象的相關知識

  C語言是面向過程的,關注的是過程,分析求解問題的步驟,通過函數調用逐步解決問題。

  C++是面向對象的,關注的是對象,將一件事拆分成不同的對象,靠對象之間的交互完成。

  對象:任何一個對象都應該具有兩個要素,即屬性和行為,對象是由一組屬性和行為構成的。如現實生活中的手機就是一個對象,它的屬性就是生產廠家,配置,顏色等等,行為就是它的功能。在一個系統中的多個對象之間通過一定的渠道相互聯系,如下圖:

  在C++中,每個對象都是由數據和函數(即操作代碼)這兩部分構成的,數據體現了屬性,函數就是對數據進行操作的,以便實現某些功能。(通過三角形這個對象的邊長,通過數學公式(函數)計算出三角形的面積(實現功能)。

2.類的定義

  C語言中,結構體中只能定義變量,在C++中,結構體中不僅可以定義變量,還可以定義函數。

  C++中的類就是將對象的屬性和行為結合在一塊,通過訪問權限將接口提供給外部用戶使用,也就是類的封裝屬性,用戶不需要知道具體的實現方法,進而提高了數據的安全性。

  類的訪問限定符:

  public(公有):修飾的成員變量在類外也可以被直接訪問

  protected(保護):在類外不能被直接訪問

  private(私有):在類外不能被直接訪問

  類訪問限定符的作用域從該訪問限定符出現的位置到下一個訪問限定符出現時為止

  注意:在C++中,為了兼容C語言的一些特性,struct的功能和C語言相同,但增加了新的功能,即可以在struct中定義函數,struct和class相同,struct默認成員為public,private不加訪問限定符時默認成員為private。

  訪問限定符只在編譯期間有作用,當數據 映射到內存的時候沒有任何訪問限定符上的區別

  類的具體實現:

(1)關鍵字:class

(2)用法:class [classname],{}為類的主體,注意類定義結束后的封號

?

1 class Student2 {3 void TestFun(){//成員函數4 }5 int a;//成員變量6 };

  類的兩種定義方法:

 ?。?)聲明和定義都放在類體中,注意:如果成員函數在類體中,則編譯器在編譯期間會將類的成員函數當做內聯函數展開(不會在Debug版本下展開,而在發布版本中展開)

1 class Student 2 { 3 public: 4 int Add(int math, int english){ 5 score=math+english; 6 cout<<a<<" "<<name<<" "<<endl; 7 } 8 public: 9 int score;10 char *name;11 };

  (2)在類中申明(放在.h文件中),在類外定義(放在.c文件中)

//Student.h文件class Student{public: void Add(int math, int english); void PrintScore();private: int score;};//Student.cvoid Student::Add(int math, int english){ score=math + english;} void Student::PrintScore(){ cout<<score<<" "<<endl;}

  注意:這種方法在定義時需要用作用域限定符“::”指定定義的函數屬于這個類作用域

3.類的實例化

  定義:用類類型創建對象的過程稱之為類的實例化

  解釋:一個類可以建立多個對象,定義類的時候操作系統不會為其開辟內存空間,只有當用類類型定義了對象之后對象才會占用物理地址空間,存儲類成員變量。打個比方,類就像現實生活中建筑物的建筑圖紙,而建筑物就是用圖紙建的對象,圖紙中的的建筑物并不真實存在,但建造(實例化)后的建筑物真實存在于客觀世界。

?

1 class Student//類 2 { 3 public: 4 int Add(int math, int english){ 5 score=math+english; 6 } 7 public: 8 char *name; 9 int score;10 int id;11 };12 13 int main()14 {15 Student S1;//對象16 S1.Add(89,94);17 cout<<score<<" "<<endl;18 return 0;19 }

?

4.類對象模型

  (1)如何計算類對象的大???

  由于類對象具有成員函數和成員變量,那么該如何計算類的大???這就是下面要講的。

  (2)類對象的存儲方式猜測

  a.如果成員函數和成員變量存儲在連續內存空間中,則當一個類的成員變量較多時,而且定義多個對象時,會將里面的成員變量與成員函數都復制一份為其開辟內存,每個對象都會保存一份代碼,但是這些對象都共用同一個函數,相同代碼保存多次,浪費空間,因此不會為每個對象都復制一份代碼。

   b.還有另一種可能情況,如下圖所示情況,只保存成員變量,成員函數放在公共的代碼段

  c.為了驗證上面兩種猜測,我們可以通過代碼求得類對象的大小

1 class Student 2 { 3 pupblic: 4 void PrintScore(){ 5 cout<<score<<" "<<endl; 6 } 7 int Add(int math , int english) 8 { 9 score=math + english;10 }11 private:12 int score;13 }14 15 int main()16 {17 Student S1;18 cout<<sizeof(S1)<<" "<<endl;19 reutrn 0;20 }

  通過上述代碼可以求得類對象的大小,進而判斷出第二種猜測是正確的。即成員函數在公共代碼區,不會為每個對象都保存一份公共的代碼。類的對象的大小求解方法和C語言中的結構體的大小相同。存在內存對齊的方式。

  一個類的大小,實際上就是該類成員變量所占內存之和,當然也要進行內存對齊,注意空類的大小,空類比較特殊,編譯器給空類一個字節來唯一標識這個類。

(3)結構體中的內存對齊規則

  a.第一個成員變量在與結構體偏移量為0的地址處

  b.其他成員要對齊到某個數字(對齊數)的整數倍地址處。注意:對齊數=編譯器默認的對齊數與該成員變量的較小值(VS中的默認對齊數是8,gcc中的默認編譯器是4

  c.結構體的總大小為:最大對齊數(所有變量的類型最大值與編譯器默認對齊數取最?。┑恼麛当短?/span>

  d.如果嵌套了結構體,嵌套的結構體對齊到自己最大的整數倍處,結構體的整體的大小就是所有最大對齊數的整數倍(含有嵌套結構體的對齊數)

  e.可以用#pragma peak(n)這個預處理指令設置默認的對齊數為n字節對齊,當然,n應該是2的k次方,不然無法對齊。

  f.通過offsetof這個宏可以求解得某個成員相對于結構體的起始位置的偏移量offsetof(類名,成員名)

5.模擬實現offsetof宏

  ?1 #define offsetof(TYPE,MEMBER) (size_t)&((TYPE *)0)->MEMBER)?

  這個宏可以根據下圖理解:

6.this指針

  (1)在看this指針前先看一個例子:

1 class Student 2 { 3 public: 4 void PrintFun(){ 5 cout<<_gender<<" "<<endl; 6 cout<<_name<<" "<<endl; 7 cout<<_age<<" "<<endl; 8 } 9 void Setstudentinfo(char *name, char* gender, int age)10 {11 strcpy(_name,name);12 strcpy(_gender,gender);13 _age=age;14 }15 private:16 char *_name;17 char* _gender;18 int _age;19 };20 21 int main()22 {23 Student S1;24 Student S2;25 S1.Setstudentinfo("TOM","male",18);26 S2.Setstudentinfo("perter","male",20);27 S1.PrintFun();28 S2.PrintFun();29 return 0;30 }

  對于上述兩個類,有這樣一個問題:

  Student類中有Setstudentinfo和PrintFun兩個成員函數,函數體中沒有關于不同對象的區分,而且這兩個成員函數存儲 在代碼公共區,那么當S1調用Setstudentinfo函數時,該函數如何知道應該設置S1對象而不是設置S2對象呢?

  C++通過引入this指針解決了這個問題,即:C++編譯器給每個“成員函數”增加了一個隱藏的指針參數,讓該指針指向當前對象(函數運行時調用該函數的對象),在函數體中的所有成員變量的操作,都是通過this指針去訪問。只不過所有的操作對于用戶是透明的,即用戶不用傳遞該參數,編譯器自動完成。

  (2)this指針的特性

  a.this指針的類型:類類型 * const this

  b.只能在“成員函數”內部使用

  c.this指針本質上其實是一個成員函數的第一個參數,是對象調用成員函數時,將對象的地址傳給this指針,所以對象中不存儲this指針。

  d.this指針是成員函數的第一個隱藏的指針形參,一般情況由編譯器通過ecx寄存器自動傳遞,不需要用戶傳遞。

  注意:this指針不能作為左值,只能在“成員函數”內部使用。并不是所有的函數的this指針都用ecx寄存器傳遞,比如不定參數函數的this指針就用eax寄存器函數壓棧的方式傳遞地址。這是因為調用函數時的調用約定不同,有的函數前面加了_thiscall(通過ecx寄存器傳遞),而有的函數前面加了_cdecl(通過壓棧的方式傳遞)

  this指針可以為空值,但如果是空值,則在成員函數內部不能訪問對象的成員變量。如下面的代碼:

1 class A 2 { 3 public: 4 int Add(int left , int right){ 5 a=left+right; 6 } 7 void PrintA(){ 8 cout<<a<<endl; 9 }10 private:11 int a;12 }13 14 int main()15 {16 A *p=NULL;//定義一個類類型的指針,指向這個A類17 p->Add(1,2);//此時類類型的指針是空值,調用它的成員函數的時候講NULL復制給this指 針,this指針為NULL,則執行Add函數時因this訪問a,所以會報錯。18 p->PrintfA();19 return 0;20 }

?

?

?

?

  

?

?

?

?

?

?

?

, 1, 0, 9);

澳洲快乐8数据分析