免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
【從零學(xué)C 11(中)】移動(dòng)語(yǔ)義、右值引用、std::move()、完美轉(zhuǎn)發(fā)等新特性

C 11


8. 默認(rèn)函數(shù)控制

C 中對(duì)于空類編譯器會(huì)生成一些默認(rèn)的成員函數(shù),比如:構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、運(yùn)算符重載、析構(gòu)函數(shù)、&和const&的重載、移動(dòng)構(gòu)造、移動(dòng)拷貝構(gòu)造等函數(shù)。

如果在類中顯式定義了,編譯器將不會(huì)重新生成默認(rèn)版本。有時(shí)候這樣的規(guī)則可能被忘記,最常見(jiàn)的是聲明了帶參數(shù)的構(gòu)造函數(shù),必要時(shí)則需要定義不帶參數(shù)的版本以實(shí)例化無(wú)參的對(duì)象。而且有時(shí)編譯器會(huì)生成,有時(shí)又不生成,容易造成混亂,于是C 11讓程序員可以控制是否需要編譯器生成。

顯式缺省函數(shù)

C 11中,可以在默認(rèn)函數(shù)定義或者聲明時(shí)加上=default,從而顯式的指示編譯器生成該函數(shù)的默認(rèn)版本,用=default修飾的函數(shù)稱為顯式缺省函數(shù)

class A{public:	A(int a)	 : _a(a)	{}		// 顯式缺省構(gòu)造函數(shù),由編譯器生成	A() = default;		// 可以選擇在類中聲明,在類外定義時(shí)讓編譯器生成默認(rèn)賦值運(yùn)算符重載	A& operator=(const A& a);private:	int _a;};A& A::operator=(const A& a) = default;		//類外定義int main(){	A a1(10);	A a2;	a2 = a1;	return 0;}

刪除默認(rèn)函數(shù)

如果能想要限制某些默認(rèn)函數(shù)的生成:

  • C 98中,是該函數(shù)設(shè)置成private,并且不完成實(shí)現(xiàn),這樣只要其他人想要調(diào)用就會(huì)報(bào)錯(cuò)。
  • C 11中更簡(jiǎn)單,只需在該函數(shù)聲明加上=delete即可,該語(yǔ)法指示編譯器不生成對(duì)應(yīng)函數(shù)的默認(rèn)版本,稱=delete修飾的函數(shù)為刪除函數(shù)。
class A{public:	A(int a)	 : _a(a)	{}		 // 禁止編譯器生成默認(rèn)的拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載	A(const A&) = delete;	A& operator(const A&) = delete;private:	int _a;};int main(){	A a1(10);	A a2(a1);	// 編譯失敗,因?yàn)樵擃悰](méi)有拷貝構(gòu)造函數(shù)		A a3(10);		a3 = a2;		// 編譯失敗,因?yàn)樵擃悰](méi)有賦值運(yùn)算符重載		return 0;}

9. 右值引用【★】

移動(dòng)語(yǔ)義

如果一個(gè)類中涉及到資源管理,用戶必須顯式提供拷貝構(gòu)造、賦值運(yùn)算符重載以及析構(gòu)函數(shù),否則編譯器將會(huì)自動(dòng)生成一個(gè)默認(rèn)的,如果遇到拷貝對(duì)象或者對(duì)象之間相互賦值,就會(huì)出錯(cuò),比如:

class String{public:	String(char* str = ""){		if (nullptr == str)			str = "";		_str = new char[strlen(str)   1];		strcpy(_str, str);	}	String(const String& s)		: _str(new char[strlen(s._str)   1])	{		strcpy(_str, s._str);	}	String& operator=(const String& s){		if (this != &s){			char* pTemp = new char[strlen(s._str)   1];			strcpy(pTemp, s._str);			delete[] _str;			_str = pTemp;		}		return *this;	}	~String(){		if (_str) delete[] _str;	}private:	char* _str;};

假設(shè)現(xiàn)在有一個(gè)函數(shù),返回值為一個(gè)String類型的對(duì)象:

String GetString(char* pStr){	String strTemp(pStr);	return strTemp;		//此時(shí)不是返回棧上對(duì)象strTemp,而是拷貝構(gòu)造一個(gè)臨時(shí)對(duì)象返回}int main(){	String s2(GetString("world"));		/* 用GetString返回的臨時(shí)對(duì)象構(gòu)造s2		s2構(gòu)造完成后臨時(shí)對(duì)象將被銷毀,因?yàn)榕R時(shí)對(duì)象臨時(shí)對(duì)象不能直接返回		因此編譯器需要拷貝構(gòu)造一份臨時(shí)對(duì)象,然后將strTemp銷毀	*/	return 0;}

上述代碼看起來(lái)沒(méi)有什么問(wèn)題,但是有一個(gè)不太盡人意的地方:GetString函數(shù)返回的臨時(shí)對(duì)象,將s2拷貝構(gòu)造成功之后,立馬被銷毀了(臨時(shí)對(duì)象的空間被釋放),再?zèng)]有其他作用;
s2在拷貝構(gòu)造時(shí),又需要分配空間,一個(gè)剛釋放一個(gè)又申請(qǐng),有點(diǎn)多此一舉。
那能否將GetString返回的臨時(shí)對(duì)象的空間直接交給s2呢?這樣s2也不需要重新開(kāi)辟空間了,代碼的效率會(huì)明顯提高。

  • 將一個(gè)對(duì)象中資源移動(dòng)到另一個(gè)對(duì)象中的方式,稱之為移動(dòng)語(yǔ)義。
  • C 11中如果需要實(shí)現(xiàn)移動(dòng)語(yǔ)義,必須使用右值引用。
String(String&& s)		//兩個(gè) &	: _str(s._str)	{		 s._str = nullptr; 	} 

C 11中的右值

右值引用,顧名思義就是對(duì)右值的引用。C 11中,右值由兩個(gè)概念組成:純右值將亡值

  • 純右值
    純右值是C 98中右值的概念,用于識(shí)別臨時(shí)變量一些不跟對(duì)象關(guān)聯(lián)的值。
    比如:常量、一些運(yùn)算表達(dá)式(1 3)等。
  • 將亡值
    聲明周期將要結(jié)束的對(duì)象。比如:在值返回時(shí)的臨時(shí)對(duì)象

右值引用

右值引用書(shū)寫(xiě)格式:

類型&& 引用變量名字 = 實(shí)體;

右值引用最長(zhǎng)常見(jiàn)的一個(gè)使用地方就是:與移動(dòng)語(yǔ)義結(jié)合,減少無(wú)必要資源的開(kāi)辟來(lái)提高代碼的運(yùn)行效率。

改造一下剛才的例子代碼演示

String&& GetString(char* pStr){	String strTemp(pStr);	return strTemp;}int main(){	String s1("hello");	String s2(GetString("world"));	return 0;}

右值引用另一個(gè)比較常見(jiàn)的地方是:給一個(gè)匿名對(duì)象取別名,延長(zhǎng)匿名對(duì)象的聲明周期。

String GetString(char* pStr){	return String(pStr);}int main(){	String&& s = GetString("hello");	return 0;}

【注】:

  1. 與引用一樣,右值引用在定義時(shí)必須初始化
  2. 通常情況下,右值引用不能引用左值。
int main(){	int a = 10;	int&& ra; // 編譯失敗,沒(méi)有進(jìn)行初始化	int&& ra = a; // 編譯失敗,a是一個(gè)左值		const int&& ra = 10;	// ra是匿名常量10的別名	return 0;}

std::move()

C 11中,std::move()函數(shù)位于<utility> 頭文件中,這個(gè)函數(shù)名字具有迷惑性,它并不搬移任何東西,唯一的功能就是將一個(gè)左值強(qiáng)制轉(zhuǎn)化為右值引用,通過(guò)右值引用使用該值,實(shí)現(xiàn)移動(dòng)語(yǔ)義。

注意:被轉(zhuǎn)化的左值,其生命周期并沒(méi)有隨著左右值的轉(zhuǎn)化而改變,即std::move轉(zhuǎn)化的左值變量left_value不會(huì)被銷毀。

  • 下面舉一個(gè)move()誤用的例子:
// 移動(dòng)構(gòu)造函數(shù)class String{	String(String&& s) 	 : _str(s._str){		s._str = nullptr;	}};int main(){	String s1("hello world");	String s2(move(s1));	String s3(s2);	return 0;}

move()更多的是用在生命周期即將結(jié)束的對(duì)象上。

【注】:為了保證移動(dòng)語(yǔ)義的傳遞,程序員在編寫(xiě)移動(dòng)構(gòu)造函數(shù)時(shí),最好使std::move轉(zhuǎn)移擁有資源的成員為右值。

注意點(diǎn)

  1. 如果將移動(dòng)構(gòu)造函數(shù)聲明為常右值引用或者返回右值的函數(shù)聲明為常量,都會(huì)導(dǎo)致移動(dòng)語(yǔ)義無(wú)法實(shí)現(xiàn)。
String(const String&&);const Person GetTempPerson();
  1. C 11中,無(wú)參構(gòu)造函數(shù) / 拷貝構(gòu)造函數(shù) / 移動(dòng)構(gòu)造函數(shù)實(shí)際上有3個(gè)版本:
Object();Object(const T&);Object(T &&);
  1. C 11中默認(rèn)成員函數(shù)
    默認(rèn)情況下,編譯器會(huì)為程序員隱式生成一個(gè)(如果沒(méi)有用到則不會(huì)生成)移動(dòng)構(gòu)造函數(shù)。如果程序員聲明了自定義的構(gòu)造函數(shù)、移動(dòng)構(gòu)造、拷貝構(gòu)造函數(shù)、賦值運(yùn)算符重載、移動(dòng)賦值、析構(gòu)函數(shù),編譯器都不會(huì)再為程序員生成默認(rèn)版本。編譯器生成的默認(rèn)移動(dòng)構(gòu)造函數(shù)實(shí)際和默認(rèn)的拷貝構(gòu)造函數(shù)類似,都是按照位拷貝(即淺拷貝)來(lái)進(jìn)行的。因此,在類中涉及到資源管理時(shí),程序員最好自己定義移動(dòng)構(gòu)造函數(shù)。其他類有無(wú)移動(dòng)構(gòu)造都無(wú)關(guān)緊要。但在C 11中,拷貝構(gòu)造/移動(dòng)構(gòu)造/賦值/移動(dòng)賦值函數(shù)必須同時(shí)提供,或者同時(shí)不提供,程序才能保證類同時(shí)具有拷貝和移動(dòng)語(yǔ)義。

完美轉(zhuǎn)發(fā)

完美轉(zhuǎn)發(fā)是指:在函數(shù)模板中,完全依照模板的參數(shù)的類型,將參數(shù)傳遞給函數(shù)模板中調(diào)用的另外一個(gè)函數(shù)。

void Func(int x){	// ......}template<typename T>void PerfectForward(T t){	Fun(t);}

PerfectForward為轉(zhuǎn)發(fā)的模板函數(shù),Func為實(shí)際目標(biāo)函數(shù),但是上述轉(zhuǎn)發(fā)還不算完美:

  • 完美轉(zhuǎn)發(fā)是:目標(biāo)函數(shù)總希望將參數(shù)按照<傳遞給轉(zhuǎn)發(fā)函數(shù)的實(shí)際類型>轉(zhuǎn)給目標(biāo)函數(shù),而不產(chǎn)生額外的開(kāi)銷,就好像轉(zhuǎn)發(fā)者不存在一樣。

  • 所謂完美:函數(shù)模板在向其他函數(shù)傳遞自身形參時(shí),如果相應(yīng)實(shí)參是左值,它就應(yīng)該被轉(zhuǎn)發(fā)為左值;如果相應(yīng)實(shí)參是右值,它就應(yīng)該被轉(zhuǎn)發(fā)為右值。這樣做是為了保留在其他函數(shù)針對(duì)轉(zhuǎn)發(fā)而來(lái)的參數(shù)的左右值屬性進(jìn)行不同處理(比如參數(shù)為左值時(shí)實(shí)施拷貝語(yǔ)義;參數(shù)為右值時(shí)實(shí)施移動(dòng)語(yǔ)義)。

C 11通過(guò)forward函數(shù)來(lái)實(shí)現(xiàn)完美轉(zhuǎn)發(fā), 比如:

void Fun(int &x) { cout << "lvalue ref" << endl; }void Fun(int &&x) { cout << "rvalue ref" << endl; }void Fun(const int &x) { cout << "const lvalue ref" << endl; }void Fun(const int &&x) { cout << "const rvalue ref" << endl; }template<typename T>void PerfectForward(T &&t) { Fun(std::forward<T>(t)); }int main(){	PerfectForward(10); // rvalue ref		int a;	PerfectForward(a); // lvalue ref	PerfectForward(std::move(a)); // rvalue ref	const int b = 8;	PerfectForward(b); // const lvalue ref	PerfectForward(std::move(b)); // const rvalue ref	return 0;}

感謝您閱讀至此,感興趣的看官們可以移步上篇與下篇,繼續(xù)了解C 11剩余新特性~

【從零學(xué)C 11(上)】列表初始化、decltype關(guān)鍵字、委派構(gòu)造等新特性
https://blog.csdn.net/qq_42351880/article/details/100140163

【從零學(xué)C 11(下)】lambda表達(dá)式、線程庫(kù)、原子操作庫(kù)等新特性
https://blog.csdn.net/qq_42351880/article/details/100144882

來(lái)源:https://www.icode9.com/content-1-423301.html
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
一文入魂:再也不用擔(dān)心我不懂C 移動(dòng)語(yǔ)義了!
復(fù)制構(gòu)造函數(shù)與賦值函數(shù)的區(qū)別
全面解析C++11新特性:現(xiàn)代編程的新起點(diǎn)
C 如何成了今天這副模樣及其語(yǔ)法從編譯器角度的合理性理解
拷貝構(gòu)造函數(shù)和賦值構(gòu)造函數(shù)的異同
const與#define的優(yōu)缺點(diǎn)?
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服