欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++之談?wù)剺?gòu)造函數(shù)的初始化列表

 更新時間:2023年04月09日 09:22:03   作者:烽起黎明  
構(gòu)造函數(shù)主要作用在于創(chuàng)建對象時為對象的成員屬性賦值,構(gòu)造函數(shù)由編譯器自動調(diào)用,無須手動調(diào)用,這篇文章詳細(xì)介紹了構(gòu)造函數(shù)的初始化列表,文章中有詳細(xì)的示例代碼,感興趣的同學(xué)可以參考閱讀

一、引入

  • 我們知道,對于下面這個類A的成員變量_a1_a2屬于【聲明】,還沒有在內(nèi)存中為其開辟出一塊空間以供存放,真正開出空間則是在【定義】的時候,那何時定義呢?也就是使用這個類A去實例化出對象的時候
  • 這個對象的空間被開出來了,難道里面的成員變量就一定開出空間了嗎?這一點我們很難去通過調(diào)試觀察
class A {
public:
	int _a1;	//聲明
	int _a2;
};
int main(void)
{
	A aa;	//	對象整體的定義,每個成員什么時候定義?
	return 0;
}

如果現(xiàn)在我在類A中加上一個const成員變量的話,初始化的時候似乎就出現(xiàn)了問題

const int _x;	

在這里插入圖片描述

  • 在搞清楚上面的問題之前你要明白const修飾的變量有哪些特點
const int i;
  • 可以看到我在這里定義了一個整型變量i,它前面是用const進行修飾的,不過編譯后報出了錯誤說【必須初始化常量對象】,因為對于const修飾的變量在聲明的時候是必須要去進行初始化的,也就是要給到一個值

在這里插入圖片描述

現(xiàn)在我們就可以來聊聊有關(guān)上面的成員變量_x為什么沒有被初始化的原因了??

  • 之前有講過,若是我們自己不去實現(xiàn)構(gòu)造函數(shù)的話,類中會默認(rèn)提供一個構(gòu)造函數(shù)來初始化成員變量,對于【內(nèi)置類型】的變量不會處理,對【自定義類型】的變量會去調(diào)用它的構(gòu)造函數(shù)。那么對于這里的_a1_a2、_x都屬于內(nèi)置類型的數(shù)據(jù),所以編譯器不會理睬,可是呢const修飾的變量又必須要初始化,這個時候該怎么辦呢╮(╯▽╰)╭

??有同學(xué)說:這還不簡單,給個缺省值不就好了

這位同學(xué)說的不錯,這個辦法確實是可以解決我們現(xiàn)在的問題,因為C++11里面為內(nèi)置類型不初始化打了一個補丁,在聲明的位置給到一個初始化值,就可以很好地防止編譯器不處理的問題

在這里插入圖片描述

但是現(xiàn)在我想問一個問題:如果不使用這個辦法呢?你有其他方法嗎?難道C++11以前就那它沒辦法了嗎?

底下的同學(xué)確實想不出什么很好的解決辦法,于是這個時候就要使用到本模塊要學(xué)習(xí)的【初始化列表】了

二、初始化的概念區(qū)分

  • 在了解【初始化列表】前,你要先知道初始化的真正含義是什么

概念:在創(chuàng)建對象時,編譯器通過調(diào)用構(gòu)造函數(shù),給對象中各個成員變量一個合適的初始值。

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 上面這個Date類是我們之前寫過的,這里有一個它的有參構(gòu)造函數(shù),雖然在這個構(gòu)造函數(shù)調(diào)用之后,對象中已經(jīng)有了一個初始值,但是不能將其稱為對對象中成員變量的初始化。構(gòu)造函數(shù)體中的語句只能將其稱為【賦初值】,而不能稱作初始化。因為初始化只能初始化一次,而構(gòu)造函數(shù)體內(nèi)可以多次賦值。

三、語法格式及使用

【初始化列表】:以一個冒號開始,接著是一個以逗號分隔的數(shù)據(jù)成員列表,每個"成員變量"后面跟一個放在括號中的初始值或表達(dá)式

  • 下面就是它的具體用法,這樣便可以通過外界傳入一些參數(shù)對年、月、日進行初始化
class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
int main(void)
{
	Date d(2023, 3, 30);
	return 0;
}

可以通過調(diào)試來觀察一下它到底是怎么走的

在這里插入圖片描述

接下去我再來說說這一塊的難點所在,準(zhǔn)備好頭腦風(fēng)暴??

  • 還是看回到我們上面的這個類A,知道了【初始化列表】這個東西,此時就不需要再聲明的部分給缺省值了,直接使用初始化列表即可。不過可以看到,對于_a1_a2我給到了缺省值,寫了初始化列表后,它們還會被初始化嗎?
class A {
public:
	A()
		:_x(1)
	{}
private:
	int _a1 = 1;	//聲明
	int _a2 = 1;

	const int _x;
};

也通過調(diào)試來看一下

在這里插入圖片描述

  • 可以看到,即使在初始化列表沒有給到_a1_a2的初始化,還是會通過給到的默認(rèn)缺省值去進行一個初始化。根據(jù)上面所學(xué),我給出以下的結(jié)論
  • 哪個對象調(diào)用構(gòu)造函數(shù),初始化列表是它所有成員變量定義的位置
  • 不管是否顯式在初始化列表寫,編譯器都會為每個變量在初始化列表進行初始化

好,接下去難度升級,請問初始化列表修改成這樣后三個成員變量初始化后的結(jié)果會是什么呢? 會是1、2、1嗎?

class A {
public:
	A()
		:_x(1)
		,_a2(1)
	{}
private:
	int _a1 = 1;	//聲明
	int _a2 = 2;

	const int _x;
};

一樣通過調(diào)試來看看

在這里插入圖片描述

  • 可以觀察到,最后初始化完后的結(jié)果為1、1、1,最令你困惑的應(yīng)該就是這個_a2了,因為我在聲明的時候給到了缺省值,然后初始化列表去進行定義的時候又去進行了一次初始化,最后的結(jié)果以初始化列表的方式為主

這里要明確的一個概念是,缺省參數(shù)只是一個備份,若是我們沒有去給到值初始化的話,編譯器就會使用這個初始值,若是我們自己給到了明確的值的話,不會去使用這個缺省值了【如果不清楚看看C++缺省參數(shù)】

接下去難度繼續(xù)升級,請問下面這樣初始化后的結(jié)果是多少?

  • 可以看到對于構(gòu)造函數(shù)我不僅寫了【初始化列表】,而且在函數(shù)體內(nèi)部還對_a1_a2進行了++和- -,那此時會有什么變化呢?
class A {
public:
	A()
		:_x(1)
		,_a2(1)
	{
		_a1++;
		_a2--;
	}
private:
	int _a1 = 1;	//聲明
	int _a2 = 2;

	const int _x;
};

如果對于上面的原理搞清楚了,那看這個就相當(dāng)于是再鞏固了一遍。也是一樣,無論是否給到缺省值都會去初始化列表走一遍,若是構(gòu)造函數(shù)內(nèi)部有語句的話就會執(zhí)行

在這里插入圖片描述

四、注意事項【?】

清楚了初始化列表該如何使用,接下去我們來說說其相關(guān)的注意事項

  • 每個成員變量在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次)
    • 可以看到,若是一個成員變量在初始化列表的地方出現(xiàn)了兩次,編譯器在編譯的時候就會報出【xxx已初始化

在這里插入圖片描述

  • 類中包含以下成員,必須放在初始化列表位置進行初始化:

const成員變量

  • 這個在前面已經(jīng)說到過了,const修飾的成員變量和構(gòu)造函數(shù)對于內(nèi)置類型不做處理產(chǎn)生了一個沖突,因此祖師爺就提出了【初始化列表】這個概念

引用成員變量

  • 第二點就是對于引用成員變量,如果有點忘記了看看C++引用通過編譯可以看出,這個引用型成員變量_z需要被初始化,它必須要引用一個值

在這里插入圖片描述

沒有默認(rèn)構(gòu)造的自定義類型成員(寫了有參構(gòu)造編譯器就不會提供默認(rèn)構(gòu)造)

  • 此時,我又寫了一個類B,將它定義出的對象作為類A的成員變量,在類B中,有一個無參的默認(rèn)構(gòu)造,也寫了相關(guān)的初始化列表去初始化_b
class B {
public:
	B()
		:_b(0)
	{}
private:
	int _b;
};
class A {
public:
	A()
		:_x(1)
		,_a1(3)
		,_a2(1)
		,_z(_a1)
	{
		_a1++;
		_a2--;
	}
private:
	int _a1 = 1;	//聲明
	int _a2 = 2;

	const int _x;
	int& _z;
	B _bb;
};
  • 通過調(diào)試來觀察就可以看到,完全符合我們前面所學(xué)的知識,若是當(dāng)前類中有自定義類型的成員變量,那在為其進行初始化的時候會去調(diào)用它的默認(rèn)構(gòu)造函數(shù)

在這里插入圖片描述

  • 但是現(xiàn)在我對這個構(gòu)造函數(shù)做了一些改動,將其變?yōu)榱擞袇⒌臉?gòu)造函數(shù),此時編譯時就報出了【沒有合適的默認(rèn)構(gòu)造函數(shù)可用
  • 我們知道默認(rèn)構(gòu)造有:無參、全缺省和編譯器自動生成的,都是不需要我們手動去調(diào)的。可以看到若是我在這里將其改為全缺省的話,就不會出問題了,因為它屬于默認(rèn)構(gòu)造函數(shù)

在這里插入圖片描述

??那對于有參構(gòu)造該如何去初始化呢?

還是可以利用到我們的【初始化列表】

在這里插入圖片描述
通過調(diào)試來看看編譯器是如何走的

在這里插入圖片描述

  • 盡量使用初始化列表初始化,因為不管你是否使用初始化列表,對于自定義類型成員變量,一定會先使用初始化列表初始化

看完了上面這一種,我們再來看看稍微復(fù)雜一些的自定義類型是否也遵循這個規(guī)則

  • 也就是我們之前寫過的Stack和MyQueue類
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10) 	//全缺省構(gòu)造
	{
		cout << "Stack()構(gòu)造函數(shù)調(diào)用" << endl;
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申請空間失敗");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	//....
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
  • 此處我們主要觀察Stack類的構(gòu)造函數(shù),因為在MyQueue中我沒有寫構(gòu)造函數(shù),為的就是使用它默認(rèn)生成的構(gòu)造函數(shù)去進行初始化。對于【內(nèi)置類型】不做處理,不過我這里給到了一個缺省值,對于【自定義類型】會去調(diào)用它的默認(rèn)構(gòu)造
class MyQueue{
public:
	//默認(rèn)生成構(gòu)造函數(shù)

private:
	Stack _pushST;
	Stack _popST;
	size_t _t = 1;
};

int main(void)
{
	MyQueue mq;
	
	return 0;
}

可能讀者有所忘卻,我們再通過調(diào)試來看一下

在這里插入圖片描述

  • 可以觀察到在初始化MyQueue類的對象時,因為內(nèi)部有兩個Stack類型的對象,所以就會去調(diào)用兩次Stack類默認(rèn)構(gòu)造來進行初始化
  • 那此時我若是將這個默認(rèn)構(gòu)造(全缺省構(gòu)造)改為有參構(gòu)造嗎,它還調(diào)得動嗎?
Stack(size_t capacity)
  • 可以看到,此時就報出了我們上面有類似遇到過的【無法引用默認(rèn)構(gòu)造函數(shù)】,為什么呢?原因就在于我們寫了,編譯器自動生成的也就不存在了,但是我又沒有傳入對應(yīng)的參數(shù)

在這里插入圖片描述

  • 此時就可以使用到我們本模塊所學(xué)習(xí)的【初始化列表】了,將需要定義的值放在初始化列表,相當(dāng)于就是為Stack類傳入了一個有參構(gòu)造的參數(shù),不過對于沒有寫在這里的_t,依舊會使用我給到的初始值1
MyQueue()
	:_pushST(10)
	,_popST(10)
{}

可以通過調(diào)試再來看看

在這里插入圖片描述

  • 當(dāng)然,如果你覺得不想要這個固定的10作為棧容量的話,也可以將這個MyQueue的構(gòu)造函數(shù)設(shè)定為有參,自己傳遞進去也是可以的

在這里插入圖片描述

  • 最后再來看一下無參構(gòu)造,也是默認(rèn)構(gòu)造的一種,在這里編譯器也會去走MyQueue的初始化列表進行初始化
//無參構(gòu)造MyQueue(){<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->}

所以可以看出,對于【內(nèi)置類型】不做處理,【自定義類型】會調(diào)用它的默認(rèn)構(gòu)造可以看出其實就是當(dāng)前類構(gòu)造函數(shù)的初始化列表在起作用

在這里插入圖片描述

在看了MyQueue類各種初始化列表的方式后,其實也可以總結(jié)出一點,無論如何不管有沒有給到缺省值,只要是顯式地寫了一個構(gòu)造函數(shù),就可以通過調(diào)試去看出編譯器都會通過【初始化列表】去進行一個初始化

  • 成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關(guān)
  • 最后再來看第四點,你認(rèn)為下面這段代碼最后打印的結(jié)果會是多少呢?1 1 嗎?
class A
{
public:
    A(int a)
       :_a1(a)
       ,_a2(_a1)
   {}
    
    void Print() {
        cout<<_a1<<" "<<_a2<<endl;
   }
private:
    int _a2;
    int _a1;
};
int main() {
    A aa(1);
    aa.Print();
}

但結(jié)果卻和我們想象的不一樣,_a1是1,_a2卻是一個隨機值,這是為什么呢?

在這里插入圖片描述

  • 通過調(diào)試可以發(fā)現(xiàn),似乎是先初始化的_a2再去初始化的_a1,對于【內(nèi)置類型】我們可以知道是編譯器是不會去進行初始化的,那若是一開始使用_a1去初始化_a2的時候,那_a2就會是一個隨機值,但是_a1卻使用傳入進來的形參a進行了初始化,那它的值就是1

在這里插入圖片描述

  • 此時我們只需要讓_a1先進行初始化即可,就不會造成隨機值的現(xiàn)象了

在這里插入圖片描述

現(xiàn)在你在翻上去把所有的調(diào)試圖一幅幅看下來就可以發(fā)現(xiàn)出初始化列表是存在順序的,它的順序不是在列表中誰先誰后的順序,而是類的成員變量聲明的順序

五、總結(jié)與提煉

最后來總結(jié)一下本文所學(xué)習(xí)的內(nèi)容??

面對必須在聲明時期初始化的成員函數(shù),我們引入了初始化列表這個東西,知道了祖師爺在構(gòu)造函數(shù)中還做了這么個小文章??。有了它,我們就再也不用擔(dān)心成員變量不會被初始化的問題了,無論是你是否給到缺省值,編譯器都會去走一遍構(gòu)造函數(shù)的初始化列表,若是沒有在定義處給到初始值,就會采用缺省值;若是給到了初始值就會采用這個值

不僅如此,初始化列表還有很多的注意事項:

  • 首先就是每個成員只能初始化一次,可以不要初始化多次哦
  • 其次就是對于三類成員一定要在初始化列表進行初始化:包括const修飾的成員變量、引用類型成員、無默認(rèn)構(gòu)造函數(shù)的自定義成員變量
  • 然后盡量使用初始化列表初始化,因為無論如何編譯器一定會走初始化列表,聲明時期的缺省值其實就是給到初始化列表使用的
  • 最后就是初始化列表中的初始化順序,與定義處的順序是無關(guān)的,和聲明處的順序有關(guān)

初始化列表是構(gòu)造函數(shù)這一塊的難點,也是祖師爺面對C++某些地方缺陷設(shè)計出來的,搞懂之后就會豁然開朗了

以上就是C++之談?wù)剺?gòu)造函數(shù)的初始化列表的詳細(xì)內(nèi)容,更多關(guān)于C++構(gòu)造函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++與Lua交互原理實例詳解

    C++與Lua交互原理實例詳解

    這篇文章主要介紹了C++與Lua交互原理實例詳解,有感興趣的同學(xué)可以研究下
    2021-02-02
  • C語言菜鳥基礎(chǔ)教程之條件判斷

    C語言菜鳥基礎(chǔ)教程之條件判斷

    本文給大家簡單介紹了下C語言中的條件判斷語句的語法和用法示例,非常簡潔實用,有需要的小伙伴可以參考下
    2017-10-10
  • C++與C語言的區(qū)別你知道嗎

    C++與C語言的區(qū)別你知道嗎

    這篇文章主要為大家詳細(xì)介紹了C++與C的區(qū)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C語言中對文件最基本的讀取和寫入函數(shù)

    C語言中對文件最基本的讀取和寫入函數(shù)

    這篇文章主要介紹了C語言中對文件最基本的讀取和寫入函數(shù),是C語言入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-08-08
  • linux c程序中獲取shell腳本輸出的實現(xiàn)方法

    linux c程序中獲取shell腳本輸出的實現(xiàn)方法

    以下是對在linux下c程序中獲取shell腳本輸出的實現(xiàn)方法進行了詳細(xì)的分析介紹,需要的朋友可以過來參考下
    2013-08-08
  • C語言實現(xiàn)通用數(shù)據(jù)結(jié)構(gòu)之通用映射(HashMap)

    C語言實現(xiàn)通用數(shù)據(jù)結(jié)構(gòu)之通用映射(HashMap)

    這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)通用數(shù)據(jù)結(jié)構(gòu)之通用映射,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • C++實踐數(shù)組類運算的實現(xiàn)參考

    C++實踐數(shù)組類運算的實現(xiàn)參考

    今天小編就為大家分享一篇關(guān)于C++實踐數(shù)組類運算的實現(xiàn)參考,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • QML與C++交互的實現(xiàn)步驟

    QML與C++交互的實現(xiàn)步驟

    本文主要介紹了QML與C++交互的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 利用C語言來求最大連續(xù)子序列乘積的方法

    利用C語言來求最大連續(xù)子序列乘積的方法

    這篇文章主要介紹了利用C語言來求最大連續(xù)子序列乘積的方法,基本的思路以外文中還附有相關(guān)ACM題目,需要的朋友可以參考下
    2015-08-08
  • 線段樹詳解以及C++實現(xiàn)代碼

    線段樹詳解以及C++實現(xiàn)代碼

    線段樹在一些acm題目中經(jīng)常見到,這種數(shù)據(jù)結(jié)構(gòu)主要應(yīng)用在計算幾何和地理信息系統(tǒng)中,這篇文章主要給大家介紹了關(guān)于線段樹以及C++實現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2021-07-07

最新評論