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

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

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

一、引入

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

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

const int _x;	

在這里插入圖片描述

  • 在搞清楚上面的問(wèn)題之前你要明白const修飾的變量有哪些特點(diǎn)
const int i;
  • 可以看到我在這里定義了一個(gè)整型變量i,它前面是用const進(jìn)行修飾的,不過(guò)編譯后報(bào)出了錯(cuò)誤說(shuō)【必須初始化常量對(duì)象】,因?yàn)閷?duì)于const修飾的變量在聲明的時(shí)候是必須要去進(jìn)行初始化的,也就是要給到一個(gè)值

在這里插入圖片描述

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

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

??有同學(xué)說(shuō):這還不簡(jiǎn)單,給個(gè)缺省值不就好了

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

在這里插入圖片描述

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

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

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

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

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

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

三、語(yǔ)法格式及使用

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

  • 下面就是它的具體用法,這樣便可以通過(guò)外界傳入一些參數(shù)對(duì)年、月、日進(jìn)行初始化
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;
}

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

在這里插入圖片描述

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

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

	const int _x;
};

也通過(guò)調(diào)試來(lái)看一下

在這里插入圖片描述

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

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

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

	const int _x;
};

一樣通過(guò)調(diào)試來(lái)看看

在這里插入圖片描述

  • 可以觀察到,最后初始化完后的結(jié)果為1、1、1,最令你困惑的應(yīng)該就是這個(gè)_a2了,因?yàn)槲以诼暶鞯臅r(shí)候給到了缺省值,然后初始化列表去進(jìn)行定義的時(shí)候又去進(jìn)行了一次初始化,最后的結(jié)果以初始化列表的方式為主

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

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

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

	const int _x;
};

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

在這里插入圖片描述

四、注意事項(xiàng)【?】

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

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

在這里插入圖片描述

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

const成員變量

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

引用成員變量

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

在這里插入圖片描述

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

  • 此時(shí),我又寫了一個(gè)類B,將它定義出的對(duì)象作為類A的成員變量,在類B中,有一個(gè)無(wú)參的默認(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;
};
  • 通過(guò)調(diào)試來(lái)觀察就可以看到,完全符合我們前面所學(xué)的知識(shí),若是當(dāng)前類中有自定義類型的成員變量,那在為其進(jìn)行初始化的時(shí)候會(huì)去調(diào)用它的默認(rèn)構(gòu)造函數(shù)

在這里插入圖片描述

  • 但是現(xiàn)在我對(duì)這個(gè)構(gòu)造函數(shù)做了一些改動(dòng),將其變?yōu)榱擞袇⒌臉?gòu)造函數(shù),此時(shí)編譯時(shí)就報(bào)出了【沒(méi)有合適的默認(rèn)構(gòu)造函數(shù)可用
  • 我們知道默認(rèn)構(gòu)造有:無(wú)參、全缺省和編譯器自動(dòng)生成的,都是不需要我們手動(dòng)去調(diào)的??梢钥吹饺羰俏以谶@里將其改為全缺省的話,就不會(huì)出問(wèn)題了,因?yàn)樗鼘儆谀J(rèn)構(gòu)造函數(shù)

在這里插入圖片描述

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

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

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

在這里插入圖片描述

  • 盡量使用初始化列表初始化,因?yàn)椴还苣闶欠袷褂贸跏蓟斜?,?duì)于自定義類型成員變量,一定會(huì)先使用初始化列表初始化

看完了上面這一種,我們?cè)賮?lái)看看稍微復(fù)雜一些的自定義類型是否也遵循這個(gè)規(guī)則

  • 也就是我們之前寫過(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申請(qǐng)空間失敗");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	//....
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
  • 此處我們主要觀察Stack類的構(gòu)造函數(shù),因?yàn)樵贛yQueue中我沒(méi)有寫構(gòu)造函數(shù),為的就是使用它默認(rèn)生成的構(gòu)造函數(shù)去進(jìn)行初始化。對(duì)于【內(nèi)置類型】不做處理,不過(guò)我這里給到了一個(gè)缺省值,對(duì)于【自定義類型】會(huì)去調(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;
}

可能讀者有所忘卻,我們?cè)偻ㄟ^(guò)調(diào)試來(lái)看一下

在這里插入圖片描述

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

在這里插入圖片描述

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

可以通過(guò)調(diào)試再來(lái)看看

在這里插入圖片描述

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

在這里插入圖片描述

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

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

在這里插入圖片描述

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

  • 成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無(wú)關(guān)
  • 最后再來(lái)看第四點(diǎn),你認(rèn)為下面這段代碼最后打印的結(jié)果會(huì)是多少呢?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卻是一個(gè)隨機(jī)值,這是為什么呢?

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

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

五、總結(jié)與提煉

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

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

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

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

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

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

相關(guān)文章

最新評(píng)論