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