象)
目錄1.類的定義一、類定義格式二、訪問限定符三、類域2.實(shí)例化一、實(shí)例化概念二、對(duì)象大小內(nèi)存對(duì)齊規(guī)則:3.this指針4.類的默認(rèn)成員函數(shù)一、構(gòu)造函數(shù)構(gòu)造函數(shù)的特點(diǎn):注意:二、析構(gòu)函數(shù)析構(gòu)函數(shù)特點(diǎn)三、拷貝構(gòu)造函數(shù)拷貝構(gòu)造函數(shù)特點(diǎn)四、賦值運(yùn)算符重載五、取地址運(yùn)算符重載operator 六、const取地址運(yùn)算符重載operator () const5.再探構(gòu)造函數(shù)6.static成員7.友元8.內(nèi)部類9.匿名對(duì)象1.類的定義一、類定義格式class為定義類的關(guān)鍵字Stack為類的名字{}中為類的主體。類體中內(nèi)容稱為類的成員類中的變量稱為類的屬性或成員變量; 類中的函數(shù)稱為類的?法或者成員函數(shù)。C中struct也可以定義類C兼容C中struct的?法同時(shí)struct升級(jí)成了類明顯的變化struct中可以定義函數(shù)?般情況下我們還是推薦?class定義類。定義在類?的成員函數(shù)默認(rèn)為inline。class A { public: void Init() { _a 10; } void Print() { cout _a endl; } private: int _a; };二、訪問限定符public修飾的成員在類外可以直接被訪問protected和private修飾的成員在類外不能直接被訪問protected和private是?樣的。訪問權(quán)限作?域從該訪問限定符出現(xiàn)的位置開始直到下?個(gè)訪問限定符出現(xiàn)時(shí)為?如果后?沒有 訪問限定符作?域就到 }即類結(jié)束。class定義成員沒有被訪問限定符修飾時(shí)默認(rèn)為privatestruct默認(rèn)為public。?般成員變量都會(huì)被限制為private/protected需要給別?使?的成員函數(shù)會(huì)放為public。class A { public: void Init() { _a 10; } void Print() { cout _a endl; } private: int _a; }; int main() { A a; a.Init(); a.Print(); //a._a;//運(yùn)行時(shí)會(huì)報(bào)錯(cuò)因?yàn)樵陬愅饷鏌o法訪問private中的成員 return 0; }運(yùn)行結(jié)果10三、類域用class/struct大括號(hào){}包裹起來的區(qū)域就是類域。類定義了?個(gè)新的作?域類的所有成員都在類的作?域中在類體外定義成員時(shí)需要使? :: 作用域操作符指明成員屬于哪個(gè)類域。class A { public: void Init(); void Print();//聲明 private: int _a; }; void A::Init()//A:: 表示Init屬于A這個(gè)類域 { _a 10; } void A::Print()//定義 { cout _a endl;2.實(shí)例化一、實(shí)例化概念?類類型在物理內(nèi)存中創(chuàng)建對(duì)象的過程稱為類實(shí)例化出對(duì)象。class Date 只是一個(gè)類型定義編譯時(shí)只記錄類域成員不分配對(duì)象內(nèi)存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;//Date類實(shí)例化出對(duì)象d1 d1.Init(2026, 10, 20); d1.Print(); return 0; }二、對(duì)象大小類實(shí)例化之后對(duì)象占用內(nèi)存字節(jié)數(shù)只算非靜態(tài)成員變量成員函數(shù)、靜態(tài)成員變量不算在對(duì)象大小里面放在靜態(tài)區(qū)里面不屬于對(duì)象內(nèi)存統(tǒng)計(jì)時(shí)只統(tǒng)計(jì)非static成員變量并遵循內(nèi)存對(duì)齊規(guī)則。內(nèi)存對(duì)齊規(guī)則:第?個(gè)成員在與結(jié)構(gòu)體偏移量為0的地址處。其他成員變量要對(duì)?到某個(gè)數(shù)字對(duì)?數(shù)的整數(shù)倍的地址處。注意對(duì)?數(shù) 編譯器默認(rèn)的?個(gè)對(duì)?數(shù) 與 該成員??的較?值。VS中默認(rèn)的對(duì)?數(shù)為8結(jié)構(gòu)體總??為最?對(duì)?數(shù)所有變量類型最?者與默認(rèn)對(duì)?參數(shù)取最?的整數(shù)倍。如果嵌套了結(jié)構(gòu)體的情況嵌套的結(jié)構(gòu)體對(duì)?到??的最?對(duì)?數(shù)的整數(shù)倍處結(jié)構(gòu)體的整體??就是所有最?對(duì)?數(shù)含嵌套結(jié)構(gòu)體的對(duì)?數(shù)的整數(shù)倍。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; cout sizeof(d1) endl;//計(jì)算d1的大小 return 0; }運(yùn)行結(jié)果123.this指針編譯器編譯后類的成員函數(shù)默認(rèn)都會(huì)在形參第?個(gè)位置增加?個(gè)當(dāng)前類類型的指針叫做this 指針。?如Date類的Init的真實(shí)原型為 void Init(Date* const this, int year, int month, int day);類的成員函數(shù)中訪問成員變量本質(zhì)都是通過this指針訪問的如Init函數(shù)中給_year賦值,this- _year year;C規(guī)定不能在實(shí)參和形參的位置顯?的寫this指針(編譯時(shí)編譯器會(huì)處理)但是可以在函數(shù)體內(nèi)顯 ?使?this指針。class Date { public: void Init(int year, int month, int day) { //this nullptr;//編譯時(shí)會(huì)報(bào)錯(cuò)因?yàn)檫@里this的類型是Date cont * this this-_year year; this-_month month; this-_day day; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; d1.Init(2026, 1, 1); d1.Print(); return 0; }4.類的默認(rèn)成員函數(shù)默認(rèn)成員函數(shù)就是??沒有顯式實(shí)現(xiàn)編譯器會(huì)?動(dòng)?成的成員函數(shù)稱為默認(rèn)成員函數(shù)。?個(gè)類我們不寫的情況下編譯器會(huì)默認(rèn)?成以下6個(gè)默認(rèn)成員函數(shù)一、構(gòu)造函數(shù)構(gòu)造函數(shù)是特殊的成員函數(shù)需要注意的是構(gòu)造函數(shù)雖然名稱叫構(gòu)造但是構(gòu)造函數(shù)的主要任務(wù)并 不是開空間創(chuàng)建對(duì)象(我們常使?的局部對(duì)象是棧幀創(chuàng)建時(shí)空間就開好了)而是對(duì)象實(shí)例化時(shí)初始化對(duì)象。構(gòu)造函數(shù)的特點(diǎn):函數(shù)名與類名相同。無返回值。對(duì)象實(shí)例化時(shí)系統(tǒng)會(huì)自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。構(gòu)造函數(shù)可以重載。如果類中沒有顯式定義構(gòu)造函數(shù)則C編譯器會(huì)自動(dòng)?成?個(gè)無參的默認(rèn)構(gòu)造函數(shù)?旦用戶顯式定義編譯器將不再?成。無參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們不寫構(gòu)造時(shí)編譯器默認(rèn)?成的構(gòu)造函數(shù)都叫做默認(rèn)構(gòu)造函 數(shù)。但是這三個(gè)函數(shù)有且只有?個(gè)存在不能同時(shí)存在。?參構(gòu)造函數(shù)和全缺省構(gòu)造函數(shù)雖然構(gòu)成 函數(shù)重載但是調(diào)?時(shí)會(huì)存在歧義。class Date { public: Date()//構(gòu)造函數(shù) { _year 1; _month 1; _day 1; } Date(int year, int month, int day)//函數(shù)重載 { _year year; _month month; _day day; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; Date d2(2026,1,1); d1.Print(); d2.Print(); return 0; }運(yùn)行結(jié)果1/1/1 2026/1/1注意:調(diào)用無參構(gòu)造時(shí)“不要寫括號(hào)”,寫了括號(hào)時(shí)就相當(dāng)于時(shí)一個(gè)函數(shù)聲明編譯時(shí)會(huì)報(bào)錯(cuò)。//Date d3();//函數(shù)聲明Date d3(void);我們不寫編譯器默認(rèn)?成的構(gòu)造對(duì)內(nèi)置類型成員變量的初始化沒有要求也就是說是是否初始化是不確定的看編譯器。class Date { public: void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; d1.Print(); return 0; }運(yùn)行結(jié)果-858993460/-858993460/-858993460對(duì)于自定義類型成員變量要求調(diào)?這個(gè)成員變量的默認(rèn)構(gòu)造函數(shù)初始化。如果這個(gè)成員變量沒有默認(rèn)構(gòu)造函數(shù)那么就會(huì)報(bào)錯(cuò)。二、析構(gòu)函數(shù)析構(gòu)函數(shù)與構(gòu)造函數(shù)功能相反析構(gòu)函數(shù)不是完成對(duì)對(duì)象本身的銷毀比如局部對(duì)象是存在棧幀的函數(shù)結(jié)束棧幀銷毀他就釋放了不需要我們管C規(guī)定對(duì)象在銷毀時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)完成對(duì)象中資源的清理釋放工作。析構(gòu)函數(shù)特點(diǎn)析構(gòu)函數(shù)名是在類名前加上字符 ~。無參數(shù)無返回值。一個(gè)類只能有一個(gè)析構(gòu)函數(shù)。若未顯示定義系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。對(duì)象生命周期結(jié)束時(shí)系統(tǒng)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)。跟構(gòu)造函數(shù)類似我們不寫編譯器自動(dòng)生成的析構(gòu)函數(shù)對(duì)內(nèi)置類型成員不做處理自定類型成員會(huì)調(diào)用他的析構(gòu)函數(shù)。還需要注意的是我們顯?寫析構(gòu)函數(shù)對(duì)于自定義類型成員也會(huì)調(diào)用他的析構(gòu)也就是說?定義類型成員無論什么情況都會(huì)?動(dòng)調(diào)用析構(gòu)函數(shù)。如果類中沒有申請(qǐng)資源時(shí)析構(gòu)函數(shù)可以不寫直接使?編譯器?成的默認(rèn)析構(gòu)函數(shù)如Date。class stack { public: stack(int n 4)//構(gòu)造函數(shù) { int* tmp (int*)malloc(sizeof(int) * n); if (tmp nullptr) { perror(malloc); exit(1); } arr tmp; top 0; capacity n; } ~stack()//析構(gòu)函數(shù) { cout~stackendl; free(arr); arr nullptr; top capacity 0; } private : int* arr; int top; int capacity; }; int main() { stack st; return 0; }運(yùn)行結(jié)果~stack三、拷貝構(gòu)造函數(shù)拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù)用一個(gè)已經(jīng)存在的同類對(duì)象去初始化新建對(duì)象拷貝構(gòu)造函數(shù)特點(diǎn)拷貝構(gòu)造函數(shù)的第?個(gè)參數(shù)必須是類類型對(duì)象的引用使用傳值方式編譯器直接報(bào)錯(cuò)因?yàn)檎Z法邏輯上會(huì)引發(fā)無窮遞歸調(diào)用。 拷貝構(gòu)造函數(shù)也可以多個(gè)參數(shù)但是第?個(gè)參數(shù)必須是類類型對(duì)象的引 用后面的參數(shù)必須有缺省值??截悩?gòu)造函數(shù)是構(gòu)造函數(shù)的?個(gè)重載。C規(guī)定自定義類型對(duì)象進(jìn)?拷貝行為必須調(diào)用拷貝構(gòu)造所以這里自定義類型傳值傳參和傳值返回都會(huì)調(diào)?拷貝構(gòu)造完成。若未顯式定義拷貝構(gòu)造編譯器會(huì)?成自動(dòng)生成拷貝構(gòu)造函數(shù)。自動(dòng)生成的拷貝構(gòu)造對(duì)內(nèi)置類型成員變量會(huì)完成值拷貝/淺拷貝(?個(gè)字節(jié)?個(gè)字節(jié)的拷貝)對(duì)?定義類型成員變量會(huì)調(diào)用他的拷貝構(gòu)造。class Date { public: Date()//構(gòu)造函數(shù) { _year 1; _month 1; _day 1; } Date(int year, int month, int day)//函數(shù)重載 { _year year; _month month; _day day; } Date(Date d)//拷貝構(gòu)造 { _year d._year; _month d._month; _day d._day; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 1); Date d2 d1; Date d3(d1); d1.Print(); d2.Print(); d3.Print(); return 0; }運(yùn)行結(jié)果2026/1/1 2026/1/1 2026/1/1注意因?yàn)镈ate類成員變量是內(nèi)置類型當(dāng)我們不寫拷貝構(gòu)造的時(shí)候會(huì)默認(rèn)使用系統(tǒng)的拷貝構(gòu)造也就是完成淺拷貝一個(gè)字節(jié)一個(gè)字節(jié)的拷貝運(yùn)行出的結(jié)果和寫了拷貝構(gòu)造的效果是一樣的。自定義類型的拷貝構(gòu)造class stack { public: stack(int n 4) { int* tmp (int*)malloc(sizeof(int) * n); if (tmp nullptr) { perror(malloc); exit(1); } arr tmp; top 0; capacity n; } ~stack() { free(arr); arr nullptr; top capacity 0; } stack(stack st) { arr (int*)malloc(sizeof(int) * st.capacity); if (arr nullptr) { perror(malloc); exit(1); } memcpy(arr, st.arr, sizeof(int) * st.top); top st.top; capacity st.capacity; } void Push(int n) { if (top capacity) { int newcapacity 2 * capacity; int* tmp (int*)realloc(arr, sizeof(int) * newcapacity); if (tmp nullptr) { perror(realloc); exit(1); } capacity newcapacity; arr tmp; } arr[top] n; } void Print() { for (int i 0; i top; i) { cout arr[i] ; } cout endl; } private : int* arr; int top; int capacity; }; int main() { stack st; st.Push(1); st.Push(2); st.Push(3); st.Push(4); st.Push(5); stack st1 st; st.Print(); st1.Print(); return 0; }運(yùn)行結(jié)果1 2 3 4 5 1 2 3 4 5四、賦值運(yùn)算符重載只能作為類的成員函數(shù)重載不能是全局函數(shù)。 運(yùn)算符重載強(qiáng)制要求成員函數(shù)因?yàn)樗枰[式this指針左操作數(shù)必須是類對(duì)象。觸發(fā)條件是兩個(gè)對(duì)象已經(jīng)存在進(jìn)行對(duì)象賦值。class Date { public: Date(int year, int month, int day) { _year year; _month month; _day day; } Date operator (const Date d1) { if (this ! d1) { _year d1._year; _month d1._month; _day d1._day; } return *this; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 1); Date d2(2026, 2, 2); d1.Print(); d2.Print(); d2 d1;//賦值重載 //Date d3 d1;//這是拷貝構(gòu)造不是賦值重載 d2.Print(); return 0; }五、取地址運(yùn)算符重載operator 對(duì)非const對(duì)象取地址返回當(dāng)前對(duì)象的this指針Date* operator() { return this; //return nullptr//返回空值 }六、const取地址運(yùn)算符重載operator () const對(duì)const對(duì)象取地址。成員函數(shù)后面的const表示這個(gè)成員函數(shù)不會(huì)修改對(duì)象成員才能被 const 對(duì)象調(diào)用。Date* operator() const { return this; //return nullptr//返回空值 }5.再探構(gòu)造函數(shù)之前我們實(shí)現(xiàn)構(gòu)造函數(shù)時(shí)初始化成員變量主要使?函數(shù)體內(nèi)賦值構(gòu)造函數(shù)初始化還有?種方式就是初始化列表初始化列表的使??式是以?個(gè)冒號(hào)開始接著是?個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表每個(gè)成員變量后?跟?個(gè)放在括號(hào)中的初始值或表達(dá)式。每個(gè)成員變量在初始化列表中只能出現(xiàn)?次語法理解上初始化列表可以認(rèn)為是每個(gè)成員變量定義初始化的地方。引用成員變量const成員變量沒有默認(rèn)構(gòu)造的類類型變量必須放在初始化列表位置進(jìn)?初始化否則會(huì)編譯報(bào)錯(cuò)。初始化列表中按照成員變量在類中聲明順序進(jìn)行初始化跟成員在初始化列表出現(xiàn)的的先后順序無關(guān)。建議聲明順序和初始化列表順序保持?致。C11?持在成員變量聲明的位置給缺省值這個(gè)缺省值主要是給沒有顯?在初始化列表初始化的成員使用的。class Date { public: Date() :_year(2026) { } void Print() { cout _year / _month / _day endl; } private: int _year ; int _month 1; int _day 1; }; int main() { Date d1; d1.Print(); return 0; }運(yùn)行結(jié)果2026/1/16.static成員?static修飾的成員變量稱之為靜態(tài)成員變量靜態(tài)成員變量?定要在類外進(jìn)行初始化。靜態(tài)成員變量為所有類對(duì)象所共享不屬于某個(gè)具體的對(duì)象不存在對(duì)象中存放在靜態(tài)區(qū)。用static修飾的成員函數(shù)稱之為靜態(tài)成員函數(shù)靜態(tài)成員函數(shù)沒有this指針。非靜態(tài)的成員函數(shù)可以訪問任意的靜態(tài)成員變量和靜態(tài)成員函數(shù)。突破類域就可以訪問靜態(tài)成員可以通過類名::靜態(tài)成員 或者 對(duì)象.靜態(tài)成員 來訪問靜態(tài)成員變量和靜態(tài)成員函數(shù)。靜態(tài)成員也是類的成員受public、protected、private 訪問限定符的限制。靜態(tài)成員變量不能在聲明位置給缺省值初始化因?yàn)槿笔≈凳莻€(gè)構(gòu)造函數(shù)初始化列表的靜態(tài)成員變量不屬于某個(gè)對(duì)象不走構(gòu)造函數(shù)初始化列表。class A { public: A() { _count; } ~A() { _count; } A(A a) { _count; } static int Getcount() { return _count; } private: static int _count;//類里面聲明 }; int A::_count 0;//類外面初始化 int main() { A a; A b; A c b; cout A::Getcount() endl; return 0; }運(yùn)行結(jié)果37.友元友元提供了?種突破類訪問限定符封裝的?式友元分為友元函數(shù)和友元類在函數(shù)聲明或者類聲明的前?加friend并且把友元聲明放到?個(gè)類的里面。外部友元函數(shù)可訪問類的私有和保護(hù)成員友元函數(shù)僅僅是?種聲明他不是類的成員函數(shù)。友元函數(shù)可以在類定義的任何地?聲明不受類訪問限定符限制。?個(gè)函數(shù)可以是多個(gè)類的友元函數(shù)。友元類中的成員函數(shù)都可以是另?個(gè)類的友元函數(shù)都可以訪問另?個(gè)類中的私有和保護(hù)成員。友元類的關(guān)系是單向的不具有交換性比如A類是B類的友元但是B類不是A類的友元。class B;//// 前置聲明否則A的友元函數(shù)聲明編譯器不認(rèn)識(shí)B class A { public: friend void func(const A a, const B b);//友元聲明 private: int _a 1; int _b 2; }; class B { public: friend void func(const A a, const B b);//友元聲明 private: int _c 3; int _d 4; }; void func(const A a, const B b) { cout a._a endl; cout b._c endl; } int main() { A a; B b; func(a, b); return 0; }運(yùn)行結(jié)果1 3class A { public: friend class B;//友元聲明 private: int _a 1; int _b 2; }; class B { public: void func(const A a) { cout a._a endl; cout _d endl; } private: int _c 3; int _d 4; }; int main() { A a; B b; b.func(a); return 0; }運(yùn)行結(jié)果1 48.內(nèi)部類如果?個(gè)類定義在另?個(gè)類的內(nèi)部這個(gè)內(nèi)部類就叫做內(nèi)部類。內(nèi)部類是?個(gè)獨(dú)?的類跟定義在全局相比他只是受外部類類域限制和訪問限定符限制所以外部類定義的對(duì)象中不包含內(nèi)部類。內(nèi)部類默認(rèn)是外部類的友元類。內(nèi)部類是獨(dú)立的類只是作用域被限制在外層類里面內(nèi)部類不默認(rèn)擁有外部類的this指針。class A { public: class B//B默認(rèn)就是A的友元 { public: void func(Aa) { cout a._a endl; cout _c endl; } private: int _c 3; int _d 4; }; private: int _a 1; int _b 2; }; int main() { A::B b; A a; b.func(a); return 0; }運(yùn)行結(jié)果1 39.匿名對(duì)象用類型(實(shí)參) 定義出來的對(duì)象叫做匿名對(duì)象相比之前我們定義的類型對(duì)象名(實(shí)參) 定義出來的叫有名對(duì)象。匿名對(duì)象生命周期只在當(dāng)前?行?般臨時(shí)定義?個(gè)對(duì)象當(dāng)前用一下即可就可以定義匿名對(duì)象。class A { public: A(int a 0) :_a(a) { cout A(int a) endl; } ~A() { cout ~A()endl; } A(const A a) { _a a._a; } private: int _a; }; int main() { //有名對(duì)象 A aa1; A aa2(10); //匿名對(duì)象--聲明周期只在當(dāng)前一行 A(); A(1); return 0; }運(yùn)行結(jié)果A(int a)//aa1的構(gòu)造 A(int a)//aa2的構(gòu)造 A(int a)//A()的構(gòu)造 ~A() //A()的析構(gòu) A(int a)//A(1)的構(gòu)造 ~A() //A(1)的析構(gòu) ~A() //aa2的析構(gòu) ~A() //aa1的析構(gòu)