=========================================================================
相關(guān)代碼gitee自取:
C語言學(xué)習(xí)日記: 加油努力 (gitee.com)
?=========================================================================
接上期:
【C++初階】五、類和對象
(日期類的完善、流運(yùn)算符重載函數(shù)、const成員、“&”取地址運(yùn)算符重載)-CSDN博客
?=========================================================================
? ? ? ? ? ? ? ? ? ? ?
目錄
????????一 . 初始化列表
構(gòu)造函數(shù)體內(nèi)賦值
初始化列表的作用和使用
初始化列表的作用:
初始化列表的使用:???????
補(bǔ)充:explicit關(guān)鍵字
二 . static成員
static成員概念:
static成員特性:
三 . 友元
友元函數(shù)
友元類
四 . 內(nèi)部類
內(nèi)部類的概念:
內(nèi)部類的特性:
補(bǔ)充:拷貝對象時(shí)的一些編譯器優(yōu)化?
補(bǔ)充:調(diào)用拷貝構(gòu)造函數(shù)還是“=”賦值運(yùn)算符重載函數(shù)
優(yōu)化一:“未初始化對象 = 內(nèi)置類型”
優(yōu)化二:“通過匿名對象調(diào)用函數(shù)”
優(yōu)化三:“通過內(nèi)置類型調(diào)用函數(shù)”
優(yōu)化四:“未初始化對象接收函數(shù)傳值返回的臨時(shí)對象”
本篇博客相關(guān)代碼:
Test.cpp文件 -- C++文件:???????
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~文章來源:http://www.zghlxwxcb.cn/news/detail-754311.html
? ? ? ? ? ? ?文章來源地址http://www.zghlxwxcb.cn/news/detail-754311.html
一 . 初始化列表
構(gòu)造函數(shù)體內(nèi)賦值
? ? ? ? ? ? ??
在創(chuàng)建對象時(shí),編譯器通過調(diào)用構(gòu)造函數(shù),給對象中各個(gè)成員變量一個(gè)合適的初始值,
在調(diào)用完構(gòu)造函數(shù),執(zhí)行完構(gòu)造函數(shù)體內(nèi)的賦值后,雖然對象中已經(jīng)有了一個(gè)初始值,
但是還不能將其稱為對對象中成員變量的初始化,
構(gòu)造函數(shù)體中的語句只能將其稱為賦初值,而不能稱為初始化。
因?yàn)?/span>初始化只能初始化一次,而構(gòu)造函數(shù)體內(nèi)是可以多次賦值的? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ??
初始化列表的作用和使用
? ? ? ? ? ? ? ? ?
初始化列表的作用:
? ? ? ? ? ? ? ??
- 初始化列表的作用可以簡單理解成:在成員變量定義時(shí)先對其進(jìn)行初始化
(先執(zhí)行初始化列表,再執(zhí)行構(gòu)造函數(shù)體中的內(nèi)容)
? ? ? ? ? ? ? ? ? ??- 當(dāng)一個(gè)類的成員變量中有:
引用變量類型、const成員變量、自定義類型成員(且該類中沒有默認(rèn)構(gòu)造函數(shù)時(shí))
當(dāng)有這三類成員變量時(shí),就必須要使用到初始化列表了
? ? ? ? ? ? ? ? ?- 對于成員變量中的: 引用變量類型 和 const成員變量,
這兩種成員變量都要求在定義時(shí)就得被初始化,構(gòu)造函數(shù)體內(nèi)賦值無法滿足這個(gè)條件;
? ? ? ? ? ? ? ? ?- 而對于成員變量中的:自定義類型成員(且該類中沒有默認(rèn)構(gòu)造函數(shù)時(shí)),
這種成員變量初始化時(shí)編譯器通常會調(diào)用其默認(rèn)構(gòu)造函數(shù),
但因?yàn)?/span>沒有默認(rèn)構(gòu)造函數(shù),只有有參構(gòu)造函數(shù),
所以需要在編譯器調(diào)用默認(rèn)構(gòu)造函數(shù)前,
就先通過初始化列表調(diào)用其有參構(gòu)造函數(shù)進(jìn)行初始化,
這也是構(gòu)造函數(shù)體內(nèi)賦值無法實(shí)現(xiàn)的
? ? ? ? ? ? ? ? ? ? ?---------------------------------------------------------------------------------------------
? ? ? ? ? ? ? ? ??
初始化列表的使用:
? ? ? ? ? ? ? ? ? ? ? ?
- 使用格式:
以一個(gè)冒號開始,接著是一個(gè)以逗號分隔的數(shù)據(jù)成員列表,
每個(gè)“成員變量”后面跟一個(gè)放在括號中的初識值或表達(dá)式
? ? ? ? ? ? ? ? ?注意事項(xiàng):
- 每個(gè)成員變量在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次)
? ? ? ? ? ? ? ? ??- 類中包含以下成員,必須放在初始化列表位置上進(jìn)行初始化:
引用成員變量、const成員變量、自定義類型成員(且該類沒有默認(rèn)構(gòu)造函數(shù)時(shí))圖示:
? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ?
- 盡量使用初始化列表進(jìn)行初始化,因?yàn)椴还苣闶欠袷褂贸跏蓟斜?/span>,
對于自定義類型成員變量,一定會先使用初始化列表初始化
? ? ? ? ? ? ??- 成員變量在類中的聲明次序就是其在初始化列表中的初始化順序,
與其在初始化列表中的先后次序無關(guān),
所以最好將成員變量的聲明次序和其在初始化列表中的先后次序保持一致圖示:
? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ??
補(bǔ)充:explicit關(guān)鍵字
? ? ? ? ? ? ? ?
- 構(gòu)造函數(shù)不僅可以構(gòu)造和初始化對象,
對于單個(gè)參數(shù)或者除第一個(gè)參數(shù)無缺省值其余均有缺省值的構(gòu)造函數(shù),
還具有隱式類型轉(zhuǎn)換的作用
? ? ? ? ? ? ?- 使用這種隱式類型轉(zhuǎn)換時(shí),代碼可讀性可能不是很好,
有些地方可能不允許出現(xiàn)這種隱式類型轉(zhuǎn)化的情況發(fā)生,
這時(shí)就可以使用 explicit關(guān)鍵字 來修飾該構(gòu)造函數(shù),這樣就會禁止構(gòu)造函數(shù)的隱式轉(zhuǎn)換圖示:
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
二 . static成員
static成員概念:
? ? ? ? ? ? ? ? ??
聲明為static的類成員稱為類的靜態(tài)成員,
用static修飾的成員變量稱為靜態(tài)成員變量;用static修飾的成員函數(shù)稱為靜態(tài)成員函數(shù)。
其中靜態(tài)成員變量是在類中聲明,在類外初始化的(實(shí)現(xiàn) / 定義)
? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ?
---------------------------------------------------------------------------------------------
? ? ? ? ? ? ? ? ??
static成員特性:
? ? ? ? ? ? ??
- 靜態(tài)成員是所有類對象共享的,不屬于某個(gè)具體的對象,存放在靜態(tài)區(qū)
? ? ? ? ? ? ? ??- 靜態(tài)成員變量必須在類外初始化(實(shí)現(xiàn) / 定義),
定義時(shí)不用添加static關(guān)鍵字,類中只是聲明
? ? ? ? ? ? ? ??- 類靜態(tài)成員可以使用 類名::靜態(tài)成員 或者 對象.靜態(tài)成員 來訪問
? ? ? ? ? ? ??- 靜態(tài)成員函數(shù)沒有隱藏的this指針,所以不能訪問任何非靜態(tài)成員,
但非靜態(tài)成員是可以訪問類的靜態(tài)成員函數(shù)的,
因?yàn)?/span>非靜態(tài)成員可以找到其對應(yīng)的類,通過類就能找到類中的靜態(tài)成員函數(shù)
(只要有辦法找到靜態(tài)成員函數(shù)的類域,就能訪問其中的靜態(tài)成員函數(shù))
? ? ? ? ? ? ?- 靜態(tài)成員也是類的成員,
所以也會受到 public 、protected 、private 訪問限定符的限制圖示:
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
三 . 友元
? ? ? ? ? ? ? ? ? ? ???????
友元提供了一種突破封裝的方式,有時(shí)能夠提供便利。
但是友元會增加代碼的耦合度,一定程度上破壞了封裝,所以友元不宜多用。
???????友元分為:友元函數(shù)和友元類? ? ? ? ? ? ? ? ??
友元函數(shù)
? ? ? ? ? ?
上期博客中我們對 “<<” 和 “>>” 兩個(gè)流運(yùn)算符進(jìn)行了重載,
實(shí)現(xiàn)了 “<<”流運(yùn)算符重載函數(shù) 和 “>>”流運(yùn)算符重載函數(shù) ,實(shí)現(xiàn)過程中,
我們發(fā)現(xiàn)無法將兩者實(shí)現(xiàn)在類中,重載定義為成員變量,
因?yàn)檫@兩個(gè)流運(yùn)算符對左操作數(shù)有一定的要求,
其左操作數(shù)一般為 cout輸出流對象 或 cin輸入流對象,
如果將其重載為成員函數(shù)的話,
成員函數(shù)隱藏的this指針就會搶占其左操作數(shù)的位置(第一個(gè)參數(shù)的位置)
? ? ? ? ? ? ? ? ? ??
- 所以要將這兩個(gè)流運(yùn)算符重載為全局函數(shù),
但這時(shí)又會導(dǎo)致類外的全局函數(shù)無法訪問類中的私有成員變量,
此時(shí)就需要通過友元來解決該問題
? ? ? ? ? ? ? ??- 友元函數(shù)可以直接訪問類的私有成員,它是定義在類外部的普通函數(shù),
不屬于任何類,但是需要在類的內(nèi)部進(jìn)行聲明,聲明時(shí)需要加 friend關(guān)鍵字?
? ? ? ? ? ? ? ? ?- 友元函數(shù)可以訪問類中的私有(private)和保護(hù)(protected)成員,
但其不是類的成員函數(shù)
? ? ? ? ? ? ?- 友元函數(shù)不能用const進(jìn)行修飾
? ? ? ? ? ??- 友元函數(shù)可以在類定義的任何地方進(jìn)行聲明,且其不受類訪問限定符的限制
? ? ? ? ? ? ?- 一個(gè)函數(shù)可以是多個(gè)類的友元函數(shù)
? ? ? ? ? ? ?- 友元函數(shù)的調(diào)用和普通函數(shù)的調(diào)用原理相同
圖示:
? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ??
友元類
? ? ? ? ? ? ? ??
友元類的所有成員函數(shù)都可以是另一個(gè)類的友元函數(shù),
都可以訪問另一個(gè)類中的非公有成員。
? ? ? ? ? ? ??
- 友元關(guān)系是單向的,不具有交換性
如:假設(shè)有A類和B類,在A類中聲明B類為其友元類,
那么就可以在B類中直接訪問A類的私有成員變量,
但想在A類中訪問B類中的私有成員變量則不行
? ? ? ? ? ? ?- 友元關(guān)系不能傳遞
如:C是B的友元,B是A的友元,則不代表C就是A的友元
? ? ? ? ? ?- 友元關(guān)系不能繼承(繼承會在之后了解到)
圖示:
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
四 . 內(nèi)部類
內(nèi)部類的概念:
? ? ? ? ? ? ?
如果將一個(gè)類定義在另一個(gè)類的內(nèi)部,那么這個(gè)類就叫做內(nèi)部類。
內(nèi)部類是一個(gè)獨(dú)立的類,它不屬于外部類,更不能通過外部類的對象去訪問內(nèi)部類的成員。
外部類對內(nèi)部類沒有任何”優(yōu)越“的訪問權(quán)限? ? ? ? ? ? ?
注意:
內(nèi)部類就是外部類的友元類,根據(jù)友元類的定義,
內(nèi)部類可以通過外部類的對象參數(shù)來訪問外部類中的所有成員。
但是外部類不是內(nèi)部類的友元? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ?
---------------------------------------------------------------------------------------------
? ? ? ? ? ? ? ? ??
內(nèi)部類的特性:
? ? ? ? ? ? ? ?
- 將內(nèi)部類定義在外部類的 public、protected、private 中都是可以的,
內(nèi)部類也會受到相應(yīng)的訪問權(quán)限限制
? ? ? ? ? ? ??- 內(nèi)部類中可以直接訪問外部類中的static成員,不需要通過外部類的 對象 / 類名
? ? ? ? ? ? ? ?- 計(jì)算外部類大小:sizeof(外部類)=外部類大小,和內(nèi)部類沒有任何關(guān)系
圖示:
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
補(bǔ)充:拷貝對象時(shí)的一些編譯器優(yōu)化?
在傳參和傳返回值的過程中,一般編譯器會做一些優(yōu)化,減少對象的拷貝,
這個(gè)在一些場景下還是非常有用的,
注:以下的優(yōu)化都是在建立在同一個(gè)表達(dá)式的基礎(chǔ)上的? ? ? ? ? ? ? ? ? ?
補(bǔ)充:
調(diào)用拷貝構(gòu)造函數(shù)還是“=”賦值運(yùn)算符重載函數(shù)? ? ? ? ? ? ? ? ?
- 調(diào)用拷貝構(gòu)造函數(shù):
當(dāng)使用 “=” ,左操作數(shù)是沒進(jìn)行初始化的對象,且右操作數(shù)是已經(jīng)存在的對象時(shí),
這種情況就會調(diào)用拷貝構(gòu)造函數(shù),通過右操作數(shù)的對象拷貝初始化左操作數(shù)的對象
? ? ? ? ? ? ??- 調(diào)用“=”賦值運(yùn)算符重載函數(shù):
當(dāng)使用“=”,且左右操作數(shù)都是已經(jīng)存在的對象時(shí),
這種情況就會調(diào)用“=”賦值運(yùn)算符重載函數(shù),將右操作數(shù)對象賦值給左操作數(shù)對象圖示:
? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ?
---------------------------------------------------------------------------------------------
? ? ? ? ? ? ? ? ??
優(yōu)化一:“未初始化對象 = 內(nèi)置類型”
? ? ? ? ? ? ? ? ?
- 在同一個(gè)表達(dá)式中,在這種情況下,
第一步:先通過內(nèi)置類型構(gòu)造出一個(gè)臨時(shí)對象
(調(diào)用:構(gòu)造函數(shù))
? ? ? ? ? ? ? ?- 第二步:再通過臨時(shí)對象拷貝構(gòu)造初始化左操作數(shù)的未初始化對象
(調(diào)用:拷貝構(gòu)造函數(shù))
? ? ? ? ? ? ? ?- 優(yōu)化:構(gòu)造函數(shù) + 拷貝構(gòu)造函數(shù) => 構(gòu)造函數(shù)
編譯器中這兩次調(diào)用實(shí)際只會調(diào)用一次構(gòu)造函數(shù)
(不同編譯器優(yōu)化不同,這里以VS2020為例)圖示:
?? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ?
---------------------------------------------------------------------------------------------
? ? ? ? ? ? ? ? ??
優(yōu)化二:“通過匿名對象調(diào)用函數(shù)”
? ? ? ? ? ? ? ? ?
- 在同一個(gè)表達(dá)式中,在這種情況下,
第一步:先初始化匿名對象
(調(diào)用:構(gòu)造函數(shù))
? ? ? ? ? ? ? ?- 第二步:再傳值傳參匿名對象調(diào)用函數(shù)
(調(diào)用:拷貝構(gòu)造函數(shù))
? ? ? ? ? ? ? ?- 優(yōu)化:構(gòu)造函數(shù) + 拷貝構(gòu)造函數(shù) => 構(gòu)造函數(shù)
編譯器中這兩次調(diào)用實(shí)際只會調(diào)用一次構(gòu)造函數(shù)
(不同編譯器優(yōu)化不同,這里以VS2020為例)圖示:
?? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ?
---------------------------------------------------------------------------------------------
? ? ? ? ? ? ? ? ??
優(yōu)化三:“通過內(nèi)置類型調(diào)用函數(shù)”
? ? ? ? ? ? ? ? ?
- 在同一個(gè)表達(dá)式中,在這種情況下,
第一步:先通過內(nèi)置類型構(gòu)造出一個(gè)臨時(shí)對象
(調(diào)用:構(gòu)造函數(shù))
? ? ? ? ? ? ? ?- 第二步:再傳值傳參臨時(shí)對象調(diào)用函數(shù)
(調(diào)用:拷貝構(gòu)造函數(shù))
? ? ? ? ? ? ? ?- 優(yōu)化:構(gòu)造函數(shù) + 拷貝構(gòu)造函數(shù) => 構(gòu)造函數(shù)
編譯器中這兩次調(diào)用實(shí)際只會調(diào)用一次構(gòu)造函數(shù)
(不同編譯器優(yōu)化不同,這里以VS2020為例)圖示:
?? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ?
---------------------------------------------------------------------------------------------
? ? ? ? ? ? ? ? ??
優(yōu)化四:“未初始化對象接收函數(shù)傳值返回的臨時(shí)對象”
? ? ? ? ? ? ? ? ?
- 在同一個(gè)表達(dá)式中,在這種情況下,
第一步:函數(shù)傳值返回時(shí)拷貝臨時(shí)對象進(jìn)行返回
(調(diào)用:拷貝構(gòu)造函數(shù))
? ? ? ? ? ? ? ?- 第二步:再通過返回的臨時(shí)對象進(jìn)行拷貝初始化對象
(調(diào)用:拷貝構(gòu)造函數(shù))
? ? ? ? ? ? ? ?- 優(yōu)化:拷貝構(gòu)造函數(shù) + 拷貝構(gòu)造函數(shù) => 拷貝構(gòu)造函數(shù)
編譯器中這兩次調(diào)用實(shí)際只會調(diào)用一次拷貝構(gòu)造函數(shù)
(不同編譯器優(yōu)化不同,這里以VS2020為例)圖示:
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
本篇博客相關(guān)代碼:
Test.cpp文件 -- C++文件:
//#define _CRT_SECURE_NO_WARNINGS 1 //包含IO流頭文件: #include <iostream> //展開std命名空間: using namespace std; //class A //{ //public: // //全缺省構(gòu)造函數(shù)(默認(rèn)構(gòu)造函數(shù)): // A(int a = 0) // //初始化列表: // :_a(a) // { // /* // * Date類中成員變量 A _aa 初始化時(shí), // * 會調(diào)用這個(gè)默認(rèn)構(gòu)造函數(shù), // * 這個(gè)默認(rèn)構(gòu)造函數(shù)再通過初始化列表 // * 對 _aa 進(jìn)行初始化 // */ // } //private: // //成員變量: // int _a; //}; // // 日期類: //class Date //{ //public: // // //Date(int year, int month, int day) // //{ // // //構(gòu)造函數(shù)體內(nèi)初始化: // // _year = year; // // _month = month; // // _day = day; // // // _ref = year; //引用變量 // // _n = 1; //const變量 // //} // // //Date(int year, int month, int day) // // //初始化列表:冒號開始,逗號分割 // // :_year(year) //初始化:年 // // ,_month(month) //初始化:月 // // ,_day(day) //初始化:日 // // ,_ref(year) //初始化:引用變量 // // ,_n(1) //初始化:const變量 // //{ // // /* // // * 引用變量 和 const變量, // // * 都必須在定義時(shí)就進(jìn)行初始化, // // * 初始化列表就可以解決這個(gè)問題 // // */ // //} // // Date(int year, int month, int day) // //初始化列表:冒號開始,逗號分割 // :_ref(year) //初始化:引用變量 // ,_n(1) //初始化:const變量 // ,_aa(10) //初始化:自定義對象 // /* // * 引用變量 和 const變量, // * 都必須在定義時(shí)就進(jìn)行初始化, // * 初始化列表就可以解決這個(gè)問題 // * // * 執(zhí)行到這里, // * 剩下的3個(gè)成員變量沒有在初始化列表中初始化, // * 但它們也已經(jīng)被定義了,只是因?yàn)槭莾?nèi)置類型, // * 編譯器會默認(rèn)給它們一個(gè)隨機(jī)值, // * 如果是自定義類型成員變量的話則會去調(diào)用 // * 其默認(rèn)構(gòu)造函數(shù) // * // * 如果該自定義類型沒有合適的默認(rèn)構(gòu)造函數(shù), // *(全缺省構(gòu)造函數(shù)、顯式定義無參構(gòu)造函數(shù)、 // * 編譯器默認(rèn)生成的構(gòu)造函數(shù)) // * 只有顯式定義的有參構(gòu)造函數(shù),那么該對象 // * 的初始化也可以放在初始化列表中。 // * 就像這里的_aa一樣,在初始化列表中, // * 直接調(diào)用其有參構(gòu)造函數(shù)進(jìn)行初始化。 // * 就是在編譯器調(diào)用其默認(rèn)構(gòu)造函數(shù)前, // * 先在初始化列表中調(diào)用其有參構(gòu)造函數(shù)進(jìn)行初始化 // * // * 初始化列表中的初始化順序和 // * 成員變量聲明的順序是一樣的, // * 所以建議初始化列表順序和聲明順序保持一致 // */ // { // //構(gòu)造函數(shù)體內(nèi)初始化: // _year = year; //初始化:年 // _month = month; //初始化:月 // _day = day; //初始化:日 // } // //private: // //聲明成員變量,未開空間: // // int _year = 1; // int _month = 1; // int _day = 1; // /* // * 這里給的1是缺省值, // * 如果 初始化列表 中沒有 // * 對應(yīng)成員變量的初始化, // * 那么該成員變量的值就會是這里設(shè)置的缺省值 // *(這里缺省值的功能就類似初始化列表) // */ // // //引用變量:必須在定義時(shí)就初始化 // int& _ref; // //const變量:必須在定義時(shí)就初始化 // const int _n; // //自定義類型對象: // A _aa; //}; // // //class Stack //{ //public: // //棧類構(gòu)造函數(shù): // Stack(int n = 2) // :_a((int*)malloc(sizeof(int)*n)) // ,_top(0) // ,_capacity(n) // /* // * 雖然說盡量使用初始化列表進(jìn)行初始化, // * 但也不是說就完全不在構(gòu)造函數(shù)體中寫代碼了, // * // * 初始化列表中也可以進(jìn)行動(dòng)態(tài)內(nèi)存開辟, // * 但有些初始化或檢查的工作,初始化列表也不能全部搞定, // * 想這里就沒有辦法對開辟的動(dòng)態(tài)空間進(jìn)行檢查 // */ // { // //…… // //構(gòu)造函數(shù)體內(nèi): // // //動(dòng)態(tài)空間檢查工作: // if (_a == nullptr) // { // perror("malloc fail"); // exit(-1); // } // // //數(shù)據(jù)拷貝工作: // memset(_a, 0, sizeof(int) * n); // // //想這里的兩種工作初始化列表就完成不了 // } // // //…… //private: // int* _a; // int _top; // int _capacity; //}; // //class MyQueue //{ //public: // MyQueue(int n1 = 10, int n2 = 20) // :_s1(n1) // ,_s2(n2) // //通過初始化列表自己控制自定義的初始化值 // //不受制于自定義類型中構(gòu)造函數(shù)的缺省值 // {} // //private: // Stack _s1; // Stack _s2; //}; // 主函數(shù): //int main() //{ // //實(shí)例化對象--定義成員變量:對象整體定義 // //每個(gè)成員變量在 初始化列表 中進(jìn)行定義 // Date d1(2023, 10, 31); // /* // * 對象中的 引用變量(_ref) 和 const變量(_n), // * 應(yīng)該在示例化對象時(shí)就已經(jīng)被定義好了, // * 所以我們實(shí)例化時(shí)不需要傳這兩個(gè)變量的參數(shù), // * 要完成這個(gè)步驟就需要依靠 初始化列表了 // */ // // MyQueue(); // MyQueue(100, 1000); // // /* // * 總結(jié)--初始化列表的作用: // * // * 1、解決必須在定義時(shí)就要求初始化的類型變量問題 // * (如:引用類型成員變量、const成員變量、 // * 自定義類型中只有有參構(gòu)造函數(shù)的初始化) // * // * 2、讓一些自定義類型的成員變量自己顯式控制初始化值 // * // * 3、盡量使用初始化列表進(jìn)行初始化, // * 初始化列表就是成員變量定義的地方, // * 在定義的地方就進(jìn)行初始化會更好一點(diǎn), // *(80%-100%的工作初始化列表能完成, // * 還有一些工作只能在函數(shù)體中完成, // * 所以要將初始化列表和函數(shù)體結(jié)合起來使用) // */ // // return 0; //} //namespace ggdpz //{ // //定義一個(gè)全局變量: // int count = 0; //統(tǒng)計(jì)一共創(chuàng)建了多少個(gè)A對象 // // /* // * 因?yàn)槲覀兺耆归_了stdC++標(biāo)準(zhǔn)庫, // * 且?guī)熘杏衏ount同名變量, // * 所以為了避免命名沖突, // * 定義一個(gè)命名空間,在命名空間中定義自己的count // */ //} A類: //class A //{ //public: // //構(gòu)造函數(shù): // A() { ++ggdpz::count; } // // //拷貝構(gòu)造函數(shù): // A(const A& t) { ++ggdpz::count; } // // /* // * 當(dāng)調(diào)用了一次構(gòu)造函數(shù)或拷貝構(gòu)造函數(shù)時(shí), // * 就說明創(chuàng)建了一個(gè)對象, // * ++count即創(chuàng)建了一個(gè)對象 // */ // // //析構(gòu)函數(shù): // ~A() { } // //private: // //}; // 創(chuàng)建一個(gè)函數(shù): //A func() //{ // //創(chuàng)建一個(gè)A對象: // A aa; // // //返回該對象: // return aa; //} // 主函數(shù): //int main() //{ // //創(chuàng)建一個(gè)A對象: // A aa; // //調(diào)用func()函數(shù): // func(); // // ggdpz::count++; // /* // * 但如果使用全局變量, // * 我這里也可以直接調(diào)用讓其+1, // * 但我們實(shí)際并沒有創(chuàng)建對象, // * 這時(shí)候統(tǒng)計(jì)的創(chuàng)建對象個(gè)數(shù)就是錯(cuò)的了 // * // * 但如果把count統(tǒng)計(jì)變量設(shè)置為成員變量的話, // * 就可以解決該問題了, // */ // // //打印創(chuàng)建了多少個(gè)對象: // cout << ggdpz::count << endl; // // return 0; //} A類: //class A //{ //public: // //構(gòu)造函數(shù): // A() { ++count; } // // //拷貝構(gòu)造函數(shù): // A(const A& t) { ++count; } // // /* // * 當(dāng)調(diào)用了一次構(gòu)造函數(shù)或拷貝構(gòu)造函數(shù)時(shí), // * 就說明創(chuàng)建了一個(gè)對象, // * ++count即創(chuàng)建了一個(gè)對象 // */ // // //析構(gòu)函數(shù): // ~A() { } // // //Get()方法: // static int GetCount() //只讀不寫 // { // /* // * 靜態(tài)成員函數(shù),沒有隱藏的this指針, // * 所以沒法在函數(shù)體中使用非靜態(tài)成員變量了, // */ // // //返回想要獲得的count: // return count; // } // //private: // //私有成員變量:: // // //統(tǒng)計(jì)創(chuàng)建對象個(gè)數(shù) // static int count; //只是聲明 // /* // * 如果是:int count = 0; // * 這里雖然是用于統(tǒng)計(jì)創(chuàng)建對象個(gè)數(shù), // * 但是每個(gè)對象中都有一個(gè)count成員變量, // * 而且都是獨(dú)立的,統(tǒng)計(jì)計(jì)數(shù)時(shí)不是加到同一個(gè)count上, // * 所以起不到統(tǒng)計(jì)的作用 // * // * 所以這里應(yīng)該使用static進(jìn)行修飾, // * 就可以讓被修飾的成員變量能夠被共享, // * 讓該類的所有對象共用該成員變量, // * 這樣就可以在成員變量上統(tǒng)計(jì)創(chuàng)建對象個(gè)數(shù)了 // * // * 成員變量使用static進(jìn)行修飾后就不支持給缺省值了, // * 即不能寫成:static int count = 0; // * 因?yàn)檫@里給的缺省值實(shí)際上是給初始化列表的, // * 而初始化列表中是初始化某個(gè)對象, // * static不是對象,不會在初始化列表中執(zhí)行, // * 所以不能這樣給缺省值 // */ //}; // ///* //* static成員變量聲明是在類中聲明, //* 但實(shí)現(xiàn)(定義)是在類外面實(shí)現(xiàn)(定義)的, //* 只能初始化一次,而且其初始化不是在實(shí)例化對象時(shí), //* 而是在執(zhí)行主函數(shù)之前就已經(jīng)初始化了, //* 這也是其不能在類中初始化列表中進(jìn)行初始化的原因 //* //* 其實(shí)本質(zhì)還是一個(gè)全局變量,只不過被放在類域中了, //* 是這個(gè)類專屬的“全局變量”(私有成員變量) //*/ //int A::count = 0; // 創(chuàng)建一個(gè)函數(shù): //A func() //{ // //創(chuàng)建一個(gè)A對象: // A aa; // // //返回該對象: // return aa; //} // 主函數(shù): //int main() //{ // //創(chuàng)建一個(gè)A對象: // A aa; // //調(diào)用func()函數(shù): // func(); // // /* // * 此時(shí)就不能直接對其進(jìn)行++了, // * 因?yàn)槭窃擃悓俚摹叭肿兞俊保?// * 屬于整個(gè)類,屬于這個(gè)類的所有對象, // * 受到了訪問限定符的影響(private), // * 所以不能直接對static成員變量進(jìn)行調(diào)用 // */ // A::count++; // // /* // * 如果static成員變量是共有的(public): // * 則可以通過類域直接訪問, // * 也可以通過對象進(jìn)行調(diào)用, // * 但通過對象進(jìn)行調(diào)用實(shí)際是找到該對象的類, // * 然后再進(jìn)行訪問的 // */ // cout << A::count << endl; //通過類域直接訪問 // cout << aa.count << endl; //通過對象進(jìn)行調(diào)用 // //aa.count -> aa是A類的對象,count是A類中的 -> A::count // // /* // * 如果static成員變量是私有的(private): // * 那要怎么獲得它呢?這時(shí)我們可以定義對應(yīng)的Get()方法, // * 再通過對象調(diào)用對應(yīng)的Get()方法來獲取static成員變量, // * Get()方法只讀不寫,只能讀不能寫 // */ // //通過對應(yīng)Get()方法獲得count的值: // cout << aa.GetCount() << endl; // // /* // * 但如果static成員變量是私有的,同時(shí)又沒有對象, // * 要怎么獲得static成員變量呢? // * // * 方式一:為了調(diào)用static成員變量而創(chuàng)建一個(gè)對象, // * 因?yàn)槭菍iT為了調(diào)用而創(chuàng)建的對象,所以調(diào)用時(shí)要-1, // * 減去創(chuàng)建這個(gè)對象的次數(shù)(不是很好的方式) // * // * 方式二:如果 類型+() 匿名對象調(diào)用對應(yīng)的Get()方法, // * 匿名對象的生命周期只有寫它的那一行,過了這一行后, // * 就會調(diào)用析構(gòu)函數(shù)“銷毀”匿名對象,匿名對象也會調(diào)用構(gòu)造函數(shù), // * 所以如果使用匿名函數(shù)查看這里的count的話,同樣需要-1 // * (也不是很好的方式) // * // * 方式三:將對應(yīng)的Get()方法設(shè)置為靜態(tài)成員函數(shù), // * 靜態(tài)成員函數(shù)的特點(diǎn):沒有this指針,方式一和方式二中, // * 因?yàn)镚et()方法中有this指針,所以必須通過對象調(diào)用this指針 // * 來使用Get()方法獲取count的值。 // * 而將其設(shè)置為靜態(tài)成員函數(shù)沒有this指針的話(在類域中), // * 調(diào)用時(shí)就不用依靠對象了,通過類域進(jìn)行調(diào)用即可 // */ // //方式一:通過有名對象 // A aa; //有名對象 // cout << aa.GetCount() - 1 << endl; //非靜態(tài)成員函數(shù) // // //方式二:通過匿名對象 // //這里的 A() 就是匿名對象 // cout << A().GetCount() - 1 << endl; //非靜態(tài)成員函數(shù) // // //方式三:通過靜態(tài)成員函數(shù) // cout << A::GetCount() << endl; //靜態(tài)成員函數(shù) // //直接通過A類域調(diào)用到對應(yīng)的Get( )方法 // cout << A().GetCount() << endl; // /* // * 設(shè)置成靜態(tài)成員函數(shù)后,依舊可以通過對象來調(diào)用, // * A()匿名對象不是通過this指針調(diào)用Get方法的, // * 而是通過A()找到對應(yīng)類域,再在類域中調(diào)用到Get方法的 // *(靜態(tài)成員函數(shù)只要能找到其對應(yīng)的類域即可進(jìn)行調(diào)用) // * // * 總結(jié): // * 一、 // * 可以將靜態(tài)成員函數(shù)和靜態(tài)成員變量看成是 // * “類域中受到了限制的全局函數(shù)和全局變量”, // * 兩者本質(zhì)沒太大的區(qū)別,使用sizeof計(jì)算有 // * 靜態(tài)成員函數(shù)或靜態(tài)成員變量的類時(shí), // * 也不會計(jì)算類中靜態(tài)成員函數(shù)或變量的大小。 // *(類的大小不包括靜態(tài)成員函數(shù)或變量) // * 使用靜態(tài)(static)修飾本質(zhì)是為了和非靜態(tài)進(jìn)行區(qū)別 // * // * 二、 // * 靜態(tài)成員函數(shù)和靜態(tài)成員變量屬于這個(gè)類的所有對象, // * 而且它們受到類域和訪問限定符的限制 // * // */ // // return 0; //} A類(單參數(shù)構(gòu)造函數(shù)): //class A //{ //public: //共有成員函數(shù): // // //(有參)構(gòu)造函數(shù): // explicit A(int a) // //初始化列表: // :_a(a) // {} // //private: //私有成員變量: // int _a = 0; //}; // 日期類(多參數(shù)構(gòu)造函數(shù)): //class Date //{ //public: //共有成員函數(shù): // // //多參數(shù)構(gòu)造函數(shù): // explicit Date(int year, int month = 1, int day = 1) // : _year(year) // , _month(month) // , _day(day) // {} // //private: //私有成員變量: // int _year; // int _month; // int _day; //}; // 主函數(shù): //int main() //{ // //正常調(diào)用構(gòu)造函數(shù)進(jìn)行初始化: // A aa1(1); // A aa2(2); // // /* // * 之前了解過的賦值運(yùn)算符重載, // * 其兩個(gè)操作數(shù)都是類類型, // * 但如果左操作數(shù)是類類型, // * 右操作數(shù)是內(nèi)置類型呢: // */ // A aa3 = 3; //正常賦值:類類型 = 內(nèi)置類型 // //左操作數(shù)為類類型,右操作數(shù)為內(nèi)置類型 // /* // * 對應(yīng)構(gòu)造函數(shù)為:A(int i) // * 此時(shí)就是將內(nèi)置類型隱式轉(zhuǎn)換成了自定義類型對象, // * 涉及到類型轉(zhuǎn)換,那就會產(chǎn)生一個(gè)臨時(shí)變量(具有常屬性), // * 這里會產(chǎn)生一個(gè) A(3) 的臨時(shí)對象(通過構(gòu)造函數(shù)產(chǎn)生), // * 再對 A(3) 進(jìn)行拷貝構(gòu)造到 aa3 (通過拷貝構(gòu)造函數(shù)), // * 所以會先調(diào)用構(gòu)造函數(shù),再調(diào)用拷貝構(gòu)造函數(shù), // * 之所以支持這個(gè)轉(zhuǎn)換, // * 是因?yàn)锳類中有 int類型的單參數(shù)構(gòu)造函數(shù) // *(單參數(shù)構(gòu)造函數(shù)的類型不同支持的隱式轉(zhuǎn)換類型也不同) // */ // // const A& ra = 3; //引用:類類型 = 內(nèi)置類型 // /* // * 這里對象ra引用的是 類型轉(zhuǎn)換時(shí)產(chǎn)生的臨時(shí)對象(變量), // * 臨時(shí)對象(變量)具有常屬性,其類型是 const A , // * 所以引用時(shí)的類型應(yīng)該是:const A& // */ // // //那么如果不想讓類類型接收隱式轉(zhuǎn)換的內(nèi)置類型該怎么辦呢? // /* // * 在構(gòu)造函數(shù)前加上關(guān)鍵字explicit, // * 即在構(gòu)造函數(shù) A(int a) 前加上變成: // * explicit A(int a) ,此時(shí)該構(gòu)造函數(shù)就不支持 // * 類類型接收隱式轉(zhuǎn)換的內(nèi)置類型了 // *(加上explicit后的構(gòu)造函數(shù)就不支持隱式類型轉(zhuǎn)換了) // *(但顯式類型轉(zhuǎn)換還是可以的) // */ // // //“特殊”多參數(shù)構(gòu)造函數(shù)正常調(diào)用初始化: // Date d1(2023, 11, 2); // // //類類型 = “(多個(gè)內(nèi)置類型)” // Date d2 = (2023, 11, 3); // //等價(jià)于:Date d2 = 3 // /* // * 對于“特殊”多參數(shù)構(gòu)造函數(shù): // * Date(int year, int month = 1, int day = 1) // * // * 這里是可以編譯過的,但d2的日期是 3/1/1 , // * 因?yàn)檫@里的 (2023,11,3) 其實(shí)是逗號表達(dá)式, // * 只以最后的值為準(zhǔn),即3,3被放在“年”的位置, // * 而后面的月和天其實(shí)是缺省值,所以結(jié)果是 3/1/1 // * // * 這里的多參數(shù)構(gòu)造函數(shù)是半缺省構(gòu)造函數(shù), // * 只有一個(gè)“年”必須給初始化值,所以該構(gòu)造函數(shù) // * 是支持用一個(gè)值進(jìn)行初始化的。 // * // * 所以只要一個(gè)對象的構(gòu)造函數(shù)能夠支持一個(gè)值完成初始化, // * 就可以通過 類類型 = 內(nèi)置類型 進(jìn)行初始化 // * (構(gòu)造函數(shù)通過缺省值進(jìn)行調(diào)整) // */ // // //多參數(shù)構(gòu)造函數(shù): // Date d4 = { 2023,11,2 }; // /* // * C++11中是支持類對象多參數(shù)初始化的, // * 不過使用的是大括號{},列表初始化, // * 初始化過程中也有產(chǎn)生臨時(shí)對象, // * 實(shí)際執(zhí)行還是通過構(gòu)造函數(shù)進(jìn)行初始化的 // */ // // const Date& d5 = { 2023,11,2 }; // //證明列表初始化也有產(chǎn)生臨時(shí)對象,其類型也有常屬性 // // return 0; //} 時(shí)間類: //class Time //{ // /* // * 日期類想訪問時(shí)間類私有成員變量, // * 應(yīng)在時(shí)間類中將日期類設(shè)置為友元類 // * // * 聲明日期類為時(shí)間類的友元類, // * 則在日期類中就可以直接訪問Time類中, // * 的私有成員變量: // */ // friend class Date; //友元類 // /* // * 友元關(guān)系是單向的, // * 此時(shí)Date日期類能訪問Time時(shí)間類的私有成員變量, // * 但Time時(shí)間類不能訪問Date日期類的 // */ // //public: //公有成員函數(shù): // Time(int hour = 0, int minute = 0, int second = 0) // : _hour(hour) // , _minute(minute) // , _second(second) // {} // //private: //私有成員變量: // int _hour; //時(shí) // int _minute; //分 // int _second; //秒 //}; // // 日期類: //class Date //{ // //友元函數(shù)聲明: // //“<<”流運(yùn)算符重載函數(shù)(類中友元聲明): // friend ostream& operator<<(ostream& _cout, const Date& d); // //“>>”流運(yùn)算符重載函數(shù)(類中友元聲明): // friend istream& operator>>(istream& _cin, Date& d); // // /* // * 友元函數(shù):讓函數(shù)能夠訪問類中的私有成員 // * // * 友元聲明雖然寫在類中,但它并不是成員函數(shù), // * 沒有隱藏的this指針,友元函數(shù)類中聲明類外定義, // * 沒有指定類域。 // * 就是一個(gè)全局函數(shù),并聲明“我是你的友元(“朋友”)” // */ // // /* // * 友元會讓耦合度變高,某種程度上破壞了封裝 // */ // //public: //共有成員函數(shù): // //全缺省構(gòu)造函數(shù): // Date(int year = 1900, int month = 1, int day = 1) // : _year(year) // , _month(month) // , _day(day) // {} // // //日期類設(shè)置當(dāng)天日期時(shí)間函數(shù): // void SetTimeOfDate(int hour, int minute, int second) // { // //直接訪問時(shí)間類中的私有成員變量: // _t._hour = hour; // _t._minute = minute; // _t._second = second; // // /* // * 將日期類設(shè)置為時(shí)間類的友元類后, // * 日期類中可以直接訪問時(shí)間類中的私有成員函數(shù) // */ // } // //private: //私有成員變量: // int _year; //年 // int _month; //月 // int _day; //日 // Time _t; //時(shí)間類對象 //}; // “<<”流運(yùn)算符重載函數(shù)(全局中實(shí)現(xiàn)): //ostream& operator<<(ostream& _cout, const Date& d) //{ // _cout << d._year << "-" << d._month << "-" << d._day; // // return _cout; //} // “>>”流運(yùn)算符重載函數(shù)(全局中實(shí)現(xiàn)): //istream& operator>>(istream& _cin, Date& d) //{ // _cin >> d._year >> d._month >> d._day; // // return _cin; //} A類: //class A //{ //private: //私有成員變量(A類): // int h; //4個(gè)字節(jié) // //public: //公有成員函數(shù): // // //在A類內(nèi)部再定義一個(gè)B類: // class B //內(nèi)部類B // { // public: //公有(B類): // int _b; // // void func() // { // A aa; // aa.h++; //直接訪問外部類的私有成員 // /* // * 因?yàn)閮?nèi)部類天生就是外部類的友元, // * 所以內(nèi)部類中可以直接訪問外部類的私有成員 // */ // } // }; // // /* // * A類和內(nèi)部類B的關(guān)系: // * 內(nèi)部類B可以認(rèn)為就是一個(gè)普通的類, // * 只是其會受到 A的類域 和 訪問限定符 的限制, // * 而且內(nèi)部類天生就會是外部類的友元(重點(diǎn)) // * // * 跟靜態(tài)成員變量和函數(shù)類似,性質(zhì)跟在全局時(shí)一樣, // * 只是受到了 類域 和 訪問限定符 的限制 // * // * C++中內(nèi)部類比較少用 // */ //}; // 主函數(shù): //int main() //{ // //計(jì)算擁有內(nèi)部類的A類的大?。?// cout << sizeof(A) << endl; // /* // * 這里A類的大小計(jì)算后是4個(gè)字節(jié), // * 也就是說計(jì)算A類時(shí)是不會計(jì)算內(nèi)部類B的 // */ // // //可以直接定義A對象: // A aa; // // //但無法直接定義內(nèi)部類B對象: // B bb; //爆紅 // // //指定其類域后就可以定義其對象了: // A::B bb; //內(nèi)部類B的權(quán)限為公有 // // A::B bb; // /* // * 指明類域后,但如果內(nèi)部類B的權(quán)限為私有, // * 那單單指明類域也無法創(chuàng)建內(nèi)部類B對象, // * 而是只能在類內(nèi)部進(jìn)行使用 // */ // // return 0; //} //擴(kuò)展內(nèi)容:一些構(gòu)造時(shí)的優(yōu)化(不同編譯器優(yōu)化不同) //A類: class A { public: //公有成員函數(shù): //構(gòu)造函數(shù)(有參): A(int a = 0) :_a(a) { cout << "A(int a)" << endl; } //拷貝構(gòu)造函數(shù): A(const A& aa) :_a(aa._a) { cout << "A(const A& aa)" << endl; } //“=”賦值運(yùn)算符重載函數(shù): A& operator=(const A& aa) { cout << "A& operator=(const A& aa)" << endl; if (this != &aa) { _a = aa._a; } return *this; } //析構(gòu)函數(shù): ~A() { cout << "~A()" << endl; } private: //私有成員變量: int _a; }; //主函數(shù): int main() { A aa1(1); //調(diào)用(有參)構(gòu)造函數(shù) A aa2(aa1); //調(diào)用拷貝構(gòu)造函數(shù) //如果有一個(gè)已經(jīng)存在的對象(aa1): A aa3 = aa1; //調(diào)用拷貝構(gòu)造函數(shù) /* * 一個(gè)已經(jīng)存在的對象拷貝初始化 * 另一個(gè)要?jiǎng)?chuàng)建的對象 -- 賦值拷貝 * * 其實(shí)就相當(dāng)于上面的:A aa2(aa1) */ //如果兩個(gè)都是已經(jīng)存在的對象(aa2 和 aa3): aa2 = aa3; //調(diào)用"="賦值運(yùn)算符重載函數(shù) return 0; } //優(yōu)化一 -- 主函數(shù): int main() { //優(yōu)化一: A aa1 = 1; //這里就是以下第三步中的b類型 /* * 第一步:先用1構(gòu)造一個(gè)臨時(shí)對象(構(gòu)造函數(shù)) * 第二步:再通過臨時(shí)對象拷貝構(gòu)造出aa1(拷貝構(gòu)造函數(shù)) * * 第三步(優(yōu)化): * 同一個(gè)表達(dá)式中(注意), * 連續(xù)構(gòu)造+構(gòu)造 / 構(gòu)造+拷貝構(gòu)造 / 拷貝構(gòu)造+拷貝構(gòu)造 * 這些操作可能會被合二為一 * a、 構(gòu)造+構(gòu)造 -> 構(gòu)造 * b、 構(gòu)造+拷貝構(gòu)造 -> 構(gòu)造 * c、 拷貝構(gòu)造+拷貝構(gòu)造 -> 拷貝構(gòu)造 * * 這里就是b類型,先調(diào)用 構(gòu)造+拷貝構(gòu)造 ,實(shí)際只調(diào)用了一次構(gòu)造函數(shù) */ return 0; } //func函數(shù): void func(A aa) {} //主函數(shù): int main() { A aa1(1); //初始化:調(diào)用構(gòu)造函數(shù) func(aa1); //傳值傳參:調(diào)用拷貝構(gòu)造函數(shù) /* * 這里雖然也是先調(diào)用構(gòu)造函數(shù), * 再調(diào)用拷貝構(gòu)造函數(shù),但這里不是再同一個(gè)表達(dá)式中 */ //優(yōu)化二: //通過匿名對象調(diào)用函數(shù): func(A(2)); /* * 這里需要先初始化匿名對象, * 調(diào)用構(gòu)造函數(shù), * 再傳值傳參匿名對象調(diào)用func函數(shù), * 調(diào)用拷貝構(gòu)造函數(shù) * * 這里跟上面不同,是在同一個(gè)表達(dá)式中, * 所以 構(gòu)造 + 拷貝構(gòu)造 -> 構(gòu)造函數(shù) * 合二為一只會調(diào)用一次構(gòu)造函數(shù) */ //優(yōu)化三(和優(yōu)化一類似): func(3); /* * 先用3構(gòu)造一個(gè)臨時(shí)對象, * 調(diào)用構(gòu)造函數(shù), * 再用臨時(shí)對象進(jìn)行傳值傳參, * 調(diào)用拷貝構(gòu)造函數(shù) * * 這里也是在同一個(gè)表達(dá)式中, * 所以所以 構(gòu)造 + 拷貝構(gòu)造 -> 構(gòu)造函數(shù) * 合二為一只會調(diào)用一次構(gòu)造函數(shù) */ return 0; } A func() { A aa; /* * 函數(shù)中初始化一個(gè)A類型對象, * 調(diào)用構(gòu)造函數(shù) */ return aa; /* * 因?yàn)槭莻髦捣祷?,所以拷貝出aa的臨時(shí)對象, * (調(diào)用拷貝構(gòu)造函數(shù)) */ } //主函數(shù): int main() { //優(yōu)化四: A aa1 = func(); /* * 傳值返回時(shí)調(diào)用了一次拷貝構(gòu)造函數(shù), * 再通過返回的臨時(shí)變量拷貝初始化aa1, * (調(diào)用拷貝構(gòu)造函數(shù)) * * 即傳值返回時(shí)調(diào)用了一次拷貝構(gòu)造函數(shù), * 然后又繼續(xù)調(diào)用一次拷貝構(gòu)造函數(shù)進(jìn)行 * 拷貝初始化對象aa1, * 所以是連續(xù)調(diào)用了兩次拷貝構(gòu)造函數(shù) * (同一個(gè)表達(dá)式中) * * 所以 拷貝構(gòu)造 + 拷貝構(gòu)造 -> 拷貝構(gòu)造 * 合二為一只會調(diào)用一次拷貝構(gòu)造函數(shù) */ A aa2; aa2 = func(); /* * 這里則不會進(jìn)行優(yōu)化, * 因?yàn)檫@時(shí)的“=”兩邊都是已經(jīng)初始化了的對象, * 所以這里會調(diào)用“=”賦值運(yùn)算符重載函數(shù)進(jìn)行賦值, * 所以不會像上面一樣被優(yōu)化 */ return 0; } /* * 總結(jié): * 再同一個(gè)表達(dá)式中: * 構(gòu)造 + 構(gòu)造 -> 構(gòu)造 * 構(gòu)造 + 拷貝構(gòu)造 -> 構(gòu)造 * 拷貝構(gòu)造 + 拷貝構(gòu)造 -> 拷貝構(gòu)造 * 現(xiàn)在的編譯器基本都會做到 * * 但一些新的編譯器可能還會跨表達(dá)式優(yōu)化, * 把中間一些不必要對象也優(yōu)化掉 -- 跨表達(dá)式優(yōu)化 * (不同編譯器可能不同) * 是一種激進(jìn)優(yōu)化,會使編譯器的維護(hù)更加復(fù)雜, * 可能會使之前的代碼在優(yōu)化后有bug */
到了這里,關(guān)于【C++初階】六、類和對象(初始化列表、static成員、友元、內(nèi)部類)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!