C++類與對象的基礎知識點詳細分析
一、什么是類和對象呢
1、類的引入
C 語言結構體中只能定義變量,在 C++ 中,結構體內不僅可以定義變量,也可以定義函數。 比如: 之前在數據結構初階中,用C 語言方式實現的棧,結構體中只能定義變量 ;現在以 C++ 方式實現, 會發(fā)現 struct 中也可以定義函數。
2、類的定義
class className { // 類體:由成員函數和成員變量組成 }; // 一定要注意后面的分號
class為定義類的關鍵字,ClassName為類的名字,{}中為類的主體,注意類定義結束時后面分號不能省略。
類體中內容稱為 類的成員: 類中的 變量 稱為 類的屬性 或 成員變量 ; 類中的 函數 稱為 類的方法 或者 成員函數 。
類的兩種定義方式:
聲明和定義全部放在類體中,需注意:成員函數如果 在類中定義 ,編譯器可能會將其當成 內 聯(lián)函數 處理。
類聲明放在 .h 文件中,成員函數定義放在 .cpp 文件中,注意: 成員函數名前需要加類名 ::。
3、類的訪問限定符
C++實現封裝的方式:用類將對象的屬性與方法結合在一塊,讓對象更加完善,通過訪問權限選擇性的將其接口提供給外部的用戶使用。
訪問限定符說明:
- public修飾的成員在類外可以直接被訪問;
- protected和private修飾的成員在類外不能直接被訪問;
- 訪問權限作用域從該訪問限定符出現的位置開始直到下一個訪問限定符出現時為止;
- 如果后面沒有訪問限定符,作用域就到 } 即類結束;
- class的默認訪問權限為private,struct為public(因為struct要兼容C) 。
4、類對象的儲存方式
我們假想:每個對象中成員變量是不同的,但是調用同一份成員函數,如果按照每實例一個對象都給成員變量和成員函數創(chuàng)造一次空間存儲,當一 個類創(chuàng)建多個對象時,每個對象中都會保存一份代碼,相同代碼保存多次,浪費空間。那么如何解決呢?
針對上面的問題,類的存儲就變成了:只保存成員變量,成員函數存放在公共的代碼段 。那么一個類的大小其實就是:實際就是該類中”成員變量”之和,當然要注意內存對齊,注意空類的大小,空類比較特殊,編譯器給了空類一個字節(jié)來唯一標識這個類的對象。
5、this指針的特性
class Date { public: void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print() { cout <<_year<< "-" <<_month << "-"<< _day <<endl; } private: int _year; // 年 int _month; // 月 int _day; // 日 }; int main() { Date d1, d2; d1.Init(2022,1,11); d2.Init(2022, 1, 12); d1.Print(); d2.Print(); return 0; }
我們知道了成員函數是放在了公共代碼段。函數體中沒有關于不同對象的區(qū)分。那么在上面的代碼中d1和d2同時掉用了Print()函數,怎么是分別打印出d1對象中的成員變量和d2對象中的成員變量呢?(當然Init函數與Print函數的區(qū)分類似)。
C++ 中通過引入 this 指針解決該問題,即: C++ 編譯器給每個 “ 非靜態(tài)的成員函數 “ 增加了一個隱藏 的指針參數,讓該指針指向當前對象 ( 函數運行時調用該函數的對象 ) ,在函數體中所有 “ 成員變量 ” 的操作,都是通過該指針去訪問。只不過所有的操作對用戶是透明的,即用戶不需要來傳遞,編 譯器自動完成 。如下圖:
this指針的特性:
- this 指針的類型:類類型 * const ,即成員函數中,不能給 this 指針賦值;
- this 指針是 “ 成員函數 ” 第一個隱含的指針形參,一般情況由編譯器通過 ecx 寄存器自動傳遞,不需要用戶傳遞;
- this 指針本質上是 “ 成員函數 ” 的形參 ,當對象調用成員函數時,將對象地址作為實參傳遞給this形參。所以 對象中不存儲 this 指針;
- 只能在 “ 成員函數 ” 的內部使用。
二、類的六個默認成員函數詳解
什么是默認成員函數呢?
默認成員函數:用戶沒有顯式實現,編譯器會生成的成員函數稱為默認成員函數。
如果一個類中什么成員都沒有,簡稱為空類。 空類中真的什么都沒有嗎?并不是,任何類在什么都不寫時,編譯器會自動生成以下6 個默認成員函數:
- 構造函數;
- 析構函數;
- 拷貝構造;
- 賦值重載;
- 普通對象取地址;
- const對象取地址。
我們來看一下各個默認的成員函數的概念及實現。本篇我們先掌握構造函數和析構函數,這兩個時相對較為麻煩和重要的,下篇我們會接著是西安剩余的默認成員函數以及類和對象剩余的重要的部分。
1、構造函數
構造函數是一個特殊的成員函數,名字與類名相同,創(chuàng)建類類型對象時由編譯器自動調用,以保證每個數據成員都有 一個合適的初始值,并且在對象整個生命周期內只調用一次。
構造函數是特殊的成員函數,需要注意的是,構造函數雖然名稱叫構造,但是構造函數的主要任務并不是開空間創(chuàng)建對象,而是初始化對象。其特征如下:
- 函數名與類名相同;
- 無返回值;
- 對象實例化時自動調用對應的構造函數;
- 構造函數可以重載。
- 如果如果類中沒有顯式定義構造函數,則C++編譯器會自動生成一個默認的無參構造函數,一但用戶顯式定義編譯器將不再生成。
我們結合著以下代碼一起理解以下。
class Date { public: // 1.無參構造函數 Date() {} // 2.帶參構造函數 Date(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; void TestDate() { Date d1; // 調用無參構造函數 Date d2(2015, 1, 1); // 調用帶參的構造函數 // 注意:如果通過無參構造函數創(chuàng)建對象時,對象后面不用跟括號,否則就成了函數聲明 // 以下代碼的函數:聲明了d3函數,該函數無參,返回一個日期類型的對象 // warning C4930: “Date d3(void)”: 未調用原型函數(是否是有意用變量定義的?) Date d3(); }
關于編譯器生成的默認成員函數,很多人都會有疑惑:不實現構造函數的情況下,編譯器會 生成默認的構造函數。但是看起來默認構造函數又沒什么用? 象調用了編譯器生成的默 認構造函數,但是 對象中的成員函數 依舊是隨機值。也就說在這里 編譯器生成的 默認構造函數并沒有什么用??
C++ 把類型分成內置類型 ( 基本類型 ) 和自定義類型。內置類型就是語言提供的數據類 型,如: int/char... ,自定義類型就是我們使用 class/struct/union等自己定義的類型。其實編譯器生成默認的構造函數會對自定類型成員 調用的它的默認成員函數,并不會對內置類型的成員變量進行初始化。
如下面代碼:Data中并沒有自己實現構造函數,系統(tǒng)會自動生成。讓后會對自定義類型成員_t調用它的默認成員函數。
class Time { public: Time() { cout << "Time()" << endl; _hour = 0; _minute = 0; _second = 0; } private: int _hour; int _minute; int _second; }; class Date { private: // 基本類型(內置類型) int _year; int _month; int _day; // 自定義類型 Time _t; }; int main() { Date d; return 0; }
無參的構造函數和全缺省的構造函數都稱為默認構造函數,并且默認構造函數只能有一個。 注意:無參構造函數、全缺省構造函數、我們沒寫編譯器默認生成的構造函數,都可以認為 是默認構造函數。
2、析構函數
通過前面構造函數的學習,我們知道一個對象是怎么來的,那一個對象又是怎么沒呢的?這里就用到了析構函數。 析構函數:與構造函數功能相反,析構函數不是完成對對象本身的銷毀,局部對象銷毀工作是由編譯器完成的。而對象在銷毀時會自動調用析構函數,完成對象中資源的清理工作。
析構函數 是特殊的成員函數,其特征如下:
- 析構函數名是在類名前加上字符 ~。
- 無參數無返回值類型;
- 一個類只能有一個析構函數。若未顯式定義,系統(tǒng)會自動生成默認的析構函數。注意:析構函數不能重載;
- 對象生命周期結束時, C++ 編譯系統(tǒng)系統(tǒng)自動調用析構函數。
我們結合下面代碼一起理解一下。
typedef int DataType; class Stack { public: Stack(size_t capacity = 3) { _array = (DataType*)malloc(sizeof(DataType) * capacity); if (NULL == _array) { perror("malloc申請空間失敗!!!"); return; } _capacity = capacity; _size = 0; } void Push(DataType data) { //CheckCapacity(); _array[_size] = data; _size++; } // 其他方法... ~Stack() { if (_array) { free(_array); _array = NULL; _capacity = 0; _size = 0; } } private: DataType* _array; int _capacity; int _size; }; void TestStack() { Stack s; s.Push(1); s.Push(2); }
關于編譯器自動生成的析構函數,是否會完成一些事情呢?編譯器生成的默認析構函數,對自定類型成員調用它的析構函數。 注意:創(chuàng)建那個類的對象則調用該類的析構函數,銷毀哪個類的對象則調用該類的析構函數。 如果類中沒有申請資源時,析構函數可以不寫,直接使用編譯器生成的默認析構函數, 有資源申請時,一定要寫,否則會造成資源泄漏。 今天的內容就到這里,需要重點理解和掌握構造函數和析構函數,后續(xù)我會更新下篇帶大家理解完類和對象ovo!
到此這篇關于C++類與對象的知識點詳細分析的文章就介紹到這了,更多相關C++類與對象內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
c++優(yōu)先隊列(priority_queue)用法詳解
這篇文章主要介紹了c++優(yōu)先隊列(priority_queue)用法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12