文章目錄
- 類的概念
- struct
- class
- class和struct的區別是什么呢?
- 類中成員函數的兩種定義方式
- 聲明和定義都在類中
- 聲明和定義分離
- 類的訪問控制和封裝
- 類的封裝特性
- 類的大小
- 結構體內存對齊規則
- 類的存儲方式
- this指針
類的概念
在C++中,類可以說是最重要的東西,因為C++一開始的定位就是c with class,也正是因為引入了類,才讓c++從c的面向過程思想轉變為面向對象的思想,關注的是對象,將一件事情拆分成不同的對象,靠對象之間的交互完成。
C++有兩種能夠創建類的方法
struct
第一種是struct,也就是從C繼承而來的,在C語言中,struct可以用來創建結構體,但是結構體中只能保存變量,而不能保存函數,在C++中,struct不僅可以用來創建類,還保留了C語言中結構體的用法
struct Date
{void PrintDate(){cout << _year << '-' << _month << '-' << _day << endl;}int _year;int _month;int _day;
};
class
class是C++中定義類的關鍵字,他的用法和struct類似。
在類中,類的元素被稱為類的成員,類中的數據被稱為成員變量或者類的屬性,類中的函數被稱為類的方法或者成員函數
class Date
{void PrintDate(){cout << _year << '-' << _month << '-' << _day << endl;}int _year;int _month;int _day;
};
class和struct的區別是什么呢?
- C++兼容C語言,所以struct不僅可以定義類,還可以當成結構體使用。
- 訪問限定不同,struct的成員默認訪問方式為public,class為private, 并且struct默認繼承為public繼承,,class為private
類中成員函數的兩種定義方式
聲明和定義都在類中
如果成員函數在類內部定義,則其是隱式的inline函數。
class Date
{void PrintDate(){cout << _year << '-' << _month << '-' << _day << endl;}int _year;int _month;int _day;
};
聲明和定義分離
在類內聲明,在類外定義,這樣能讓類更加簡潔,不那么臃腫,通常都是使用這種方式。
class Date
{void PrintDate();int _year;int _month;int _day;
};//需要通過作用域運算符指定這個方法屬于Date類
void Date::PrintDate()
{cout << _year << '-' << _month << '-' << _day << endl;
}
類的訪問控制和封裝
訪問限定符有三種,分別是public,private,protected
- public修飾的成員在類外可以直接被訪問。
- protected和private修飾的成員在類外不能直接被訪問,protected修飾內容派生類可以訪問,private不可以。
- 訪問權限作用域從該訪問限定符出現的位置開始直到下一個訪問限定符出現時為止。
類的封裝特性
封裝:將數據和操作數據的方法進行有機結合,隱藏對象的屬性和實現細節,僅對外公開接口來和對象進行 交互。
封裝本質上是一種管理:我們如何管理兵馬俑呢?如果什么都不管,兵馬俑可能會被隨意破壞。那么我們首先建了一座房子把兵馬俑給封裝起來。但是我們目的并不是全封裝起來,不讓別人看。所以我們開放了售票通道,可以通過買票突破封裝、在合理的監管機制下進去參觀。類也是一樣,我們將類數據和方法都封裝一下。不想給別人肆意修改,我們使用protected/private把成員封裝起來。同時開放一些共有的成員函數對成員合理的訪問機制。所以封裝本質是一種管理。
一般對于對象的屬性,我們不希望別人對它進行修改,所以會用private將其封裝,但是我們又需要給別人提供獲取它的接口,所以會對方法和接口給與public屬性。
類的大小
class Date
{
public:void PrintDate();private:char a;int _year;int _month;int _day;
};
我們可以看到,類中有成員函數,有成員變量,那么類的大小是如何計算的呢?
int main()
{Date d;cout << sizeof(d) << endl;
}
這里用sizeof查看大小,發現只有成員變量的大小(三個int類型一個char類型,3*4+1=13,這里的16是因為內存對齊偏移了3個字節),而沒有成員函數的大小
結構體內存對齊規則
- 第一個成員在與結構體偏移量為0的地址處。
- 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。
注意:對齊數 = 編譯器默認的一個對齊數 與 該成員大小 兩者中的較小值。(如int大小為4,VS中默認的對齊數為8,取4) - 結構體總大小為:最大對齊數(所有變量類型最大者與默認對齊參數取最小,本例中所有變量類型最大者是int,大小為4,與默認對齊參數取最小,則為4)的整數倍。
- 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。
類的存儲方式
為什么會只保存了成員變量呢?這里就要引申出類的存儲方式。
對于上面的類,如果我們將類實例化為多個對象,實際上這些對象不同的地方只有它的屬性,它們調用的方法都是完全相同的, 為了不重復保存這些代碼,C++只保存了屬性,而將方法放到了公共的代碼段中,來避免了空間的浪費。
同時,如果一個類中什么都沒有,編譯器會給這個類一個字節,來唯一標識這個類。
總結:類的大小就是成員變量之和(需要考慮內存對齊)
this指針
void Date::PrintDate()
{cout << _year << '-' << _month << '-' << _day << endl;
}
對于類中的成員函數,我們并沒有將對象的屬性傳給他,那么他是如何能夠獲取這些屬性的呢?
C++編譯器給每個“非靜態的成員函數“增加了一個隱藏的指針參數(this指針),讓該指針指向當前對象(函數運行時調用該函數的對象),在函數體中所有對成員變量的操作,都是通過該指針去訪問。只不過所有的操作對用戶是透明的,即用戶不需要來傳遞,編譯器自動完成。
其實類的成員函數中隱含了一個this指針,所以在編譯器處理的時候,上面的代碼等價于
void Date::PrintDate(Date * const this)
{cout << this->_year << '-' << this->_month << '-' << this->_day << endl;
}
- 這個this指針的類型是類類型 * const this,之所以是頂層const的原因是因為this指針唯一指向對象地址。
- this指針只能在成員函數中使用
- this指針本質上其實是一個成員函數的形參,是對象調用成員函數時,將對象地址作為實參傳遞給this形參。所以對象中不存儲this指針,this指針存在寄存器中。
- this指針是成員函數第一個隱含的指針形參,一般情況由編譯器通過ecx寄存器自動傳遞,不需要用戶傳遞,
同時,如果this指針為空時,成員函數使用到了this指針,則會崩潰,如果沒使用,則正常運行