久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

C++中臨時對象的常見產(chǎn)生情況及其解決的方案

這篇文章主要是探討常見的臨時對象產(chǎn)生的情況,及其如何避免和解決這種臨時對象產(chǎn)生的方式。具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

在C++中很容易就寫出一些代碼,這些代碼的特點就是偷偷的給你產(chǎn)生了一些臨時對象,導(dǎo)致臨時對象會調(diào)用拷貝構(gòu)造函數(shù),賦值運算符,析構(gòu)函數(shù),假如該對象還有繼承的話,也會調(diào)用父類的拷貝構(gòu)造函數(shù),賦值運算賦函數(shù)等。這些臨時對象所調(diào)用的函數(shù),都是不必要的開銷,也就是說,我本意不想你給我調(diào)用這些函數(shù)的,但你編譯器卻給我偷偷的調(diào)用了,就是由于我程序員寫代碼產(chǎn)生臨時對象而產(chǎn)生的。

所以臨時對象產(chǎn)生的話題也應(yīng)運而生,這篇文章主要是探討常見的臨時對象產(chǎn)生的情況,及其如何避免和解決這種臨時對象產(chǎn)生的方式。

1. 以值傳遞的方式給函數(shù)傳參

這種是最常見的產(chǎn)生嶺師對象的方式了。

以值傳遞的方式給函數(shù)傳參這種方式會直接調(diào)用對象的拷貝構(gòu)造函數(shù),生成一個臨時對象傳參給函數(shù)。當臨時對象銷毀時候,也是函數(shù)形參銷毀,也是函數(shù)執(zhí)行完后,就會調(diào)用該臨時對象的析構(gòu)函數(shù)。此時,無論是調(diào)用拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù),都是額外的開銷。

(驗證是否調(diào)用拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù),可以在書寫拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)驗證)
(驗證是否為臨時對象可以通過再函數(shù)內(nèi)部修改形參的值,在函數(shù)外部打印看看是否修改成功)

驗證臨時對象的而外開銷(1)


# include<iostream>
using namespace std;

class Person{
public:
	Preson()
	{	
		cout << "無參構(gòu)造函數(shù)!" << endl;
	}	
	Person(int a)
	{ 
		m_age = a;
		cout << "有參構(gòu)造函數(shù)!" << endl;
	}
	Person(const Person &p)
	{ 
		m_age = p.m_age;
		cout << "拷貝構(gòu)造函數(shù)!" << endl;
	}
	~Person()
	{
		cout << "析構(gòu)函數(shù)!" << endl;
	}
	int fun(Person p) //普通的成員函數(shù),注意參數(shù)是以值的方式調(diào)用的
	{
		p.m_age = 20; //這里修改對外界沒有印象
		return p.m_age;
	}
	int m_age;	
};

int main()
{
	Person p(10);//初始化
	p.fun(p);
	return 0;
}

先來預(yù)測一下調(diào)用函數(shù)的次數(shù):也就是我們本意想調(diào)用的方式:
會執(zhí)行一次 Person的有參構(gòu)造函數(shù);
會執(zhí)行一次Person的析構(gòu)函數(shù);

于此同時我們看看,編譯結(jié)果實際情況:

在這里插入圖片描述

和我們預(yù)期并不一樣!!! 多了一次拷貝構(gòu)造函數(shù)和一次析構(gòu)函數(shù)。這兩個函數(shù)并不是我們希望要得,或者說,這個多余函數(shù)開銷是不必要的;

產(chǎn)生的原因也很好理解:

由于 fun成員函數(shù)里面的形參是Person p,這樣會導(dǎo)致在調(diào)用這個fun函數(shù)時候,會傳遞過去的是實參的復(fù)制品,臨時對象,并不是外面main函數(shù)的實參,這里可以在fun函數(shù)里修改一樣形參就可以發(fā)現(xiàn),外面的實參沒發(fā)生改變。

所以產(chǎn)生的臨時對象給形參傳參時候,在我們看來類似 Person p = p;實際上是Person p = temp;而這句 Person p = temp;就會發(fā)生拷貝構(gòu)造函數(shù)啦,于此同時 fun函數(shù)調(diào)用結(jié)束后,p的聲明周期也就結(jié)束,所以還會多調(diào)用析構(gòu)函數(shù)。

解決方案

如何避免這種臨時對象的產(chǎn)生呢?

只要把值傳遞的方式修改為引用傳遞的方式即可。這樣既不會調(diào)用拷貝構(gòu)造函數(shù),也不會調(diào)用多一次臨時對象的析構(gòu)函數(shù)。減少額外不必要的開銷。

所以我們在函數(shù)形參設(shè)計時候,能夠用引用就用引用的方式,因為這樣可以減少對象的復(fù)制操作,減少而外的開銷。

代碼不驗證啦,因為比較簡單,可以自行驗證,修改 fun函數(shù)里形參為 Person& p;即可。

2. 類型轉(zhuǎn)換成臨時對象 / 隱式類型轉(zhuǎn)換保證函數(shù)調(diào)用成功

這種方式就是并且把類型轉(zhuǎn)化前的對象當作了形參傳遞給構(gòu)造函數(shù),生成臨時對象臨時對象結(jié)束后就會調(diào)用析構(gòu)函數(shù)。

驗證臨時對象的而外開銷(2)

代碼依舊是上一個代碼,只是在main函數(shù)做了不一樣的動作


# include<iostream>
using namespace std;

class Person{
public:
	Preson()
	{	
		cout << "無參構(gòu)造函數(shù)!" << endl;
	}	
	Person(int a)
	{ 
		m_age = a;
		cout << "有參構(gòu)造函數(shù)!" << endl;
	}
	Person(const Person &p)
	{ 
		m_age = p.m_age;
		cout << "拷貝構(gòu)造函數(shù)!" << endl;
	}
	~Person()
	{
		cout << "析構(gòu)函數(shù)!" << endl;
	}
	int fun(Person p) //普通的成員函數(shù),注意參數(shù)是以值的方式調(diào)用的
	{
		p.m_age = 20; //這里修改對外界沒有印象
		return p.m_age;
	}
	int m_age;	
};

int main()
{
	Person p;
	p = 1000; 
	return 0;
}

首先預(yù)測一下該代碼執(zhí)行的結(jié)果:

首先 調(diào)用一次無參構(gòu)造函數(shù),一次析構(gòu)函數(shù)。

其次看看編譯器運行的結(jié)果:

在這里插入圖片描述

為啥會多出一個有參構(gòu)造函數(shù)呢和析構(gòu)函數(shù)呢?

其實是由于 p = 1000;這句引起的,這里p的類型為 Person,而 1000為 int 類型,很明顯類型不一致。
編譯器其實偷偷的進行了類型轉(zhuǎn)換,如何轉(zhuǎn)換呢?看編譯器的調(diào)用都可以發(fā)現(xiàn),其實就是創(chuàng)建一個臨時對象,這個臨時對象調(diào)用了有參構(gòu)造函數(shù),并且把 這個1000作為形參,傳入有參構(gòu)造函數(shù),當這個函數(shù)調(diào)用結(jié)束后,對象也就銷毀了,所以臨時對象會調(diào)用析構(gòu)函數(shù)。

解決方案

其實很簡單的:
只要把單參數(shù)構(gòu)造函數(shù)的復(fù)制(復(fù)制)語句,改為初始化語句就行。
那什么是復(fù)制語句和初始化語句呢?
兩者的區(qū)別就是
一個是創(chuàng)建對象同時賦值對象,也就是說創(chuàng)建時候就馬上初始化,這就是初始化;
一個是創(chuàng)建對象時候不賦值對象,而是等對象創(chuàng)建好,過后使用再賦值對象,這就是賦值語句啦;

那么我們只需要把:


	Person p;
	p = 1000; 
	修改為:
	Person p = 1000;

這樣就不會有多一次的有參構(gòu)造和析構(gòu)的開銷了。

3. 函數(shù)返回對象時候

在函數(shù)返回對象時候,會創(chuàng)建一個臨時對象接收這個對象;從而調(diào)用了拷貝構(gòu)造函數(shù),和析構(gòu)函數(shù)。
當你調(diào)用函數(shù),沒有接收返回值時候,就會調(diào)用析構(gòu)函數(shù),因為都沒有人接收返回值了,自然而然析構(gòu)了。當你調(diào)用時候,有接收返回值時候,這個時候,并不會多調(diào)用一次析構(gòu)函數(shù),而是直接把臨時對象返回值,給了接受返回值的變量來接收。

驗證臨時對象的而外開銷(3)

代碼:


# include<iostream>
using namespace std;

class Person{
public:
	Preson()
	{	
		cout << "無參構(gòu)造函數(shù)!" << endl;
	}	
	Person(int a)
	{ 
		m_age = a;
		cout << "有參構(gòu)造函數(shù)!" << endl;
	}
	Person(const Person &p)
	{ 
		m_age = p.m_age;
		cout << "拷貝構(gòu)造函數(shù)!" << endl;
	}
	~Person()
	{
		cout << "析構(gòu)函數(shù)!" << endl;
	}
	int fun(Person p) //普通的成員函數(shù),注意參數(shù)是以值的方式調(diào)用的
	{
		p.m_age = 20; //這里修改對外界沒有印象
		return p.m_age;
	}
	int m_age;	
};
Person test(Person & p)
{
	Person p1; //這里會調(diào)用無參構(gòu)造函數(shù)和結(jié)束的一次析構(gòu)函數(shù)
	p1.m_age = p.m_age;
	return p1; //這里會多調(diào)用一次臨時拷貝和析構(gòu)函數(shù)
}
int main()
{
	Person p;
	test(p);
	return 0;
}

看看執(zhí)行結(jié)果:

在這里插入圖片描述

其實很好理解:就是以值的方式返回時候,就會多調(diào)用一次拷貝構(gòu)造和析構(gòu)函數(shù);
結(jié)果中的第一個析構(gòu)時test函數(shù)里p1對象的析構(gòu),第二個析構(gòu)時 返回值時候臨時對象的析構(gòu);第三個析構(gòu)時main函數(shù)里p對象的析構(gòu);

請注意我的test函數(shù)在調(diào)用時候,我并沒有給返回值,此時;當我以返回只接受時候,就會有不一樣結(jié)果:不一樣的地方就是,少了一次析構(gòu)函數(shù),其實少的這次析構(gòu)函數(shù)時test函數(shù)里返回值產(chǎn)生的臨時對象,因為,當你有對象接收返回值時候,就會直接把test函數(shù)里返回值臨時對象給初始化接收返回值對象;

即,我修改main函數(shù)的代碼:


int main()
{
	Person p;
	Person p2 = test(p); //此時test返回值臨時對象并不會析構(gòu),
						//因為這里把臨時對象直接初始化了p2;
	return 0;
}

在這里插入圖片描述

可以說時編譯器優(yōu)化手段吧。本來說 p2對象因該也是需要調(diào)用多一次拷貝構(gòu)造函數(shù)的,但是由于有臨時對象的初始化,所以p2對象就直接接管臨時對象了。所以上面結(jié)果最后的析構(gòu)函數(shù),其實時p2對象的析構(gòu),并不是臨時對象的析構(gòu)。

解決方案

其實也很簡單的解決辦法:有兩種:

  • 當我們在接收函數(shù)返回的對象時候,可以用右值引用接收,因為該函數(shù)返回值是一個臨時變量,用一個右值引用接收它,使得它的生命周期得以延續(xù),這樣就少調(diào)用一次析構(gòu)函數(shù)的開銷。(當然普通的對象接收也是可以)
  • 當我們在設(shè)計函數(shù)里的return 語句中,不是返回創(chuàng)建好的對象,而是返回我們臨時創(chuàng)建的對象,即使用retturn 類類型(形參); 這個時候,就可以直接避免 return 對象;返回時候又要調(diào)用多一次構(gòu)造函數(shù)。

這兩種行為就可以避免了構(gòu)造函數(shù)和析構(gòu)函數(shù)的產(chǎn)生。

但是,右值引用我還沒有寫到這文章,所以先不講右值引用的方案,講第二種方案:
也就是設(shè)計函數(shù)返回語句 return時候,不要直接返回對象,而是返回臨時對象,這個臨時對象。


把這個代碼修改:
Person test(Person & p)
{
	Person p1; //這里會調(diào)用無參構(gòu)造函數(shù)和結(jié)束的一次析構(gòu)函數(shù)
	p1.m_age = p.m_age;
	return p1; //這里會多調(diào)用一次臨時拷貝和析構(gòu)函數(shù)
}

修改為:
Person test(Person &p)
{
	return Person(p.m_age);//直接返回臨時對象,可以減少
}

在這里插入圖片描述

其實,只要以值得形式返回對象都會調(diào)用多一次拷貝構(gòu)造函數(shù),所以我們盡量避免這種情況,用合適的方式解決它。

到此這篇關(guān)于C++中臨時對象的常見產(chǎn)生情況及其解決的方案的文章就介紹到這了,更多相關(guān)C++ 臨時對象內(nèi)容請搜索html5模板網(wǎng)以前的文章希望大家以后多多支持html5模板網(wǎng)!

【網(wǎng)站聲明】本站部分內(nèi)容來源于互聯(lián)網(wǎng),旨在幫助大家更快的解決問題,如果有圖片或者內(nèi)容侵犯了您的權(quán)益,請聯(lián)系我們刪除處理,感謝您的支持!

相關(guān)文檔推薦

這篇文章主要介紹了++ 設(shè)計模式的基本原則,主要的目標是實現(xiàn)最終目的,高內(nèi)聚,低耦合,開放封閉原則類的改動是通過增加代碼進行的,感興趣的小伙伴可參考下面文章的具體內(nèi)容
這篇文章主要介紹了C++基于OpenCV手勢識別的實現(xiàn)源碼,這里用到背景減法模型知識,具體實例代碼跟隨小編一起看看吧
下面小編就為大家?guī)硪黄钊肜斫鈉++指針的指針和指針的引用。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考,一起跟隨小編過來看看吧
C++ 提供了異常機制,讓我們能夠捕獲運行時錯誤,本文就詳細的介紹了C++異常處理入門,具有一定的參考價值,感興趣的小伙伴們可以參考一下
這篇文章主要給大家介紹了關(guān)于C/C++中的內(nèi)存模型和名稱空間詳解,文中通過示例代碼介紹的非常詳細,對大家學習或者使用c/c++具有一定的參考學習價值,需要的朋友們下面隨著小編來
推箱子想必是很多人童年時期的經(jīng)典游戲,我們依舊能記得抱個老人機娛樂的場景,下面這篇文章主要給大家介紹了關(guān)于如何利用c++寫一個簡單的推箱子小游戲的相關(guān)資料,需要的朋友可以
主站蜘蛛池模板: 成人在线观看免费视频 | 国产精品九九九 | 亚洲精品久久久一区二区三区 | 影音先锋中文字幕在线观看 | 亚洲一区二区中文字幕 | 久久久久久亚洲精品 | 九九亚洲 | 日韩无| 日韩中文字幕在线 | 欧美日韩精品一区二区三区蜜桃 | 羞羞色在线观看 | 欲色av| 欧美综合国产精品久久丁香 | www国产亚洲精品久久网站 | 日韩和的一区二区 | 中文字幕在线一区 | 老头搡老女人毛片视频在线看 | 伊人精品久久久久77777 | 亚洲永久免费观看 | 一区二区三区福利视频 | 国产成人免费视频网站视频社区 | 亚洲日韩中文字幕一区 | 国产综合在线视频 | aaaaaaa片毛片免费观看 | 99视频在线 | 国产精品成人一区二区三区夜夜夜 | 国产午夜高清 | 爱草在线 | 羞羞视频网站免费观看 | 国产在线看片 | 国产国产精品久久久久 | 久久久毛片 | 国产美女一区二区 | 奇米影视77 | 亚洲免费视频播放 | 国产最新精品视频 | 成人精品在线观看 | 国产精品久久av | 日韩高清在线 | 亚洲精品一区在线 | 国产一级片91 |