亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++11新特性之右值引用與完美轉(zhuǎn)發(fā)詳解

 更新時(shí)間:2022年09月23日 15:01:56   作者:賣(mài)寂寞的小男孩  
C++11標(biāo)準(zhǔn)為C++引入右值引用語(yǔ)法的同時(shí),還解決了一個(gè)短板,即使用簡(jiǎn)單的方式即可在函數(shù)模板中實(shí)現(xiàn)參數(shù)的完美轉(zhuǎn)發(fā)。本文就來(lái)講講二者的應(yīng)用,需要的可以參考一下

一、左值與右值

顧名思義,左值就是只能放在等號(hào)左邊的值,右值是只能放在等號(hào)右邊的值。

在C++Prime一書(shū)中,對(duì)左值和右值的劃分為,左值是一個(gè)表示數(shù)據(jù)的表達(dá)式,右值是一個(gè)即將銷(xiāo)毀的值(通常稱為將亡值)。比如我們定義的一個(gè)變量就是一個(gè)左值,而字面常量,表達(dá)式返回值,傳值返回函數(shù)的返回值就是右值。

	10;//右值
	int a = 10;//a是左值
	add(2, 3);//右值
	x+y;//右值
	const int a;//左值

注意,const類型的變量是不能放在等號(hào)左側(cè)來(lái)為它賦值的,但是他是一個(gè)左值。

這里給出一個(gè)區(qū)分兩者的方式:可以取地址的就是左值,不能取地址的就是右值!

二、左值引用與右值引用

我們之前所寫(xiě)的引用都是左值引用符號(hào)是&,左值引用的底層是使用指針,它的作用是為對(duì)象取一個(gè)別名。

而右值引用就是給右值取別名,它的符號(hào)是&&,右值引用開(kāi)辟了空間,得到的一個(gè)對(duì)象是左值。

	int a = 10;
	int& d = a;//左值引用
	int&& e = 10;//右值引用
	int&& f = a + 1;
	int&& c = add(2, 3);

左值引用不能給右值取別名,右值引用也不能給左值取別名。但是如果對(duì)左值進(jìn)行move(),對(duì)左值引用加上const是可以這樣進(jìn)行的。

move的意思就是保證除了賦值和銷(xiāo)毀之外,不再使用該左值,即將a的屬性轉(zhuǎn)移到了e中,對(duì)左值move后是一共右值。

	int&& c = a;//右值引用不能給左值取別名
	int& d = add(3, 4);//左值引用不能給右值取別名
	int&& e = move(a);//當(dāng)對(duì)左值加move的時(shí)候可以
	const int& f = add(3, 4);//當(dāng)對(duì)引用加const后可以取別名

同時(shí)右值引用不像左值引用一樣具有傳遞性:

	int&& a = 10;
	a=20;
	cout<<&a<<endl;
	//int&& b = a;//錯(cuò)誤

這是因?yàn)閍是一個(gè)左值,我們可以打印a的地址,右值經(jīng)過(guò)引用后得到的對(duì)象是一個(gè)左值。因此我們是可以對(duì)a進(jìn)行賦值的。

三、右值引用應(yīng)用

1.移動(dòng)構(gòu)造與移動(dòng)賦值

移動(dòng)構(gòu)造與移動(dòng)賦值在C++11中已經(jīng)加入了STL容器的函數(shù)中:

string(string&& str) //移動(dòng)構(gòu)造
string& operator=(string&& str)//移動(dòng)賦值

移動(dòng)構(gòu)造與移動(dòng)賦值都是向函數(shù)中傳入右值引用,它們的本質(zhì)與右值基本相同,就是將一個(gè)將亡值的數(shù)據(jù)轉(zhuǎn)移給另一個(gè)值。

我們可以在函數(shù)string中模擬實(shí)現(xiàn)一下移動(dòng)構(gòu)造和移動(dòng)賦值,它們的本質(zhì)就是調(diào)用swap函數(shù)完成賦值,而不是使用strcpy創(chuàng)建一個(gè)新對(duì)象。

1.模擬實(shí)現(xiàn)的string

為了方便觀察,我們使用自己模擬實(shí)現(xiàn)的string來(lái)進(jìn)行說(shuō)明:

namespace my_string
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		//構(gòu)造函數(shù)
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			cout << "string(char* str)" << endl;

			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		// s1.swap(s2)
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
		// 拷貝構(gòu)造
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(const string& s) -- 深拷貝" << endl;
			string tmp(s._str);
			swap(tmp);
		}
		// 移動(dòng)構(gòu)造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 資源轉(zhuǎn)移" << endl;
			this->swap(s);
		}
		// 移動(dòng)賦值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 轉(zhuǎn)移資源" << endl;
			swap(s);
			return *this;
		}
		//賦值
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 深拷貝" << endl;
			string tmp(s);
			swap(tmp);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		//下標(biāo)訪問(wèn)
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		//調(diào)換順序
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}
		//插入
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		//string operator+=(char ch)
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string operator+(char ch)
		{
			string tmp(*this);
			push_back(ch);

			return tmp;
		}
		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做標(biāo)識(shí)的\0
	};
	my_string::string to_string(int value)
	{
		my_string::string str;
		while (value)
		{
			int val = value % 10;
			str += ('0' + val);
			value /= 10;
		}
		reverse(str.begin(), str.end());
		return str;
	}
}

2.移動(dòng)構(gòu)造

當(dāng)我們調(diào)用to_string的時(shí)候:

my_string::string ret = my_string::to_string(1234);

當(dāng)我們不添加移動(dòng)構(gòu)造的時(shí)候,可以發(fā)現(xiàn)最終進(jìn)行的是一次深拷貝和一次淺拷貝:

這里發(fā)現(xiàn)只調(diào)用了一次拷貝構(gòu)造,這是因?yàn)榫幾g器做了優(yōu)化,如果不優(yōu)化的話,str拷貝構(gòu)造臨時(shí)對(duì)象,然后臨時(shí)對(duì)象作為to_string的返回值再拷貝構(gòu)造給ret。其實(shí)是發(fā)生了兩次拷貝構(gòu)造。

但是編譯器做了優(yōu)化之后,在to_string函數(shù)快結(jié)束時(shí),返回前直接用str構(gòu)造ret。

當(dāng)我們加入拷貝構(gòu)造之后,會(huì)發(fā)現(xiàn)只發(fā)生了一次移動(dòng)構(gòu)造就可以了:

其實(shí)在這一過(guò)程中編譯器也做了優(yōu)化,str先拷貝構(gòu)造形成一個(gè)臨時(shí)對(duì)象,再由臨時(shí)對(duì)象進(jìn)行移動(dòng)構(gòu)造賦值給ret。

編譯器做了優(yōu)化之后,將str直接當(dāng)成左值(相當(dāng)于move了一下),然后進(jìn)行移動(dòng)構(gòu)造生成ret。

通過(guò)觀察打印結(jié)果可以發(fā)現(xiàn),顯然移動(dòng)構(gòu)造沒(méi)有再開(kāi)辟空間,而是直接將數(shù)據(jù)進(jìn)行轉(zhuǎn)移,節(jié)省了空間,由臨時(shí)變量進(jìn)行拷貝構(gòu)造給ret還會(huì)創(chuàng)建一個(gè)新的對(duì)象,消耗空間。

3.移動(dòng)賦值

	my_string::string ret;
	ret = my_string::to_string(1234);//調(diào)用移動(dòng)賦值

當(dāng)不使用移動(dòng)賦值的時(shí)候,以上代碼是兩段深拷貝實(shí)現(xiàn)的:

首先str會(huì)調(diào)用移動(dòng)構(gòu)造,生成臨時(shí)對(duì)象,然后臨時(shí)對(duì)象再調(diào)用賦值拷貝構(gòu)造(深拷貝),定義ret。

當(dāng)引入移動(dòng)賦值之后,這個(gè)過(guò)程就變成了str調(diào)用移動(dòng)構(gòu)造生成臨時(shí)對(duì)象,臨時(shí)對(duì)象再通過(guò)移動(dòng)運(yùn)算符重載生成ret,整個(gè)過(guò)程中沒(méi)有一次深拷貝。

C++11中,所有STL容器中,都會(huì)提供一個(gè)右值引用的版本。

四、默認(rèn)移動(dòng)構(gòu)造和移動(dòng)賦值重載函數(shù)

與六大成員函數(shù)一樣,編譯器在一定的條件下,也會(huì)生成自己的默認(rèn)移動(dòng)構(gòu)造函數(shù),只不過(guò)生成的條件更加復(fù)雜:

1.如果你自己沒(méi)有實(shí)現(xiàn)移動(dòng)構(gòu)造函數(shù),并且沒(méi)有實(shí)現(xiàn)析構(gòu)函數(shù),拷貝構(gòu)造,拷貝賦值構(gòu)造中的任意一個(gè)。那么編譯器會(huì)自動(dòng)生成一個(gè)默認(rèn)構(gòu)造函數(shù)。默認(rèn)生成的移動(dòng)構(gòu)造函數(shù),對(duì)內(nèi)置類型進(jìn)行直接拷貝,對(duì)于自定義類型,如果有對(duì)應(yīng)的移動(dòng)構(gòu)造函數(shù)就調(diào)用其對(duì)應(yīng)的移動(dòng)構(gòu)造函數(shù),如果沒(méi)有那么調(diào)用拷貝構(gòu)造。

2.如果你沒(méi)自己實(shí)現(xiàn)移動(dòng)賦值重載函數(shù),且沒(méi)有實(shí)現(xiàn)析構(gòu)函數(shù),拷貝構(gòu)造,拷貝賦值重載中的任何一個(gè),編譯器會(huì)自動(dòng)生成一個(gè)移動(dòng)賦值重載函數(shù)。默認(rèn)生成的移動(dòng)賦值重載函數(shù),對(duì)內(nèi)置類型直接進(jìn)行賦值,對(duì)于自定義類型,如果有對(duì)應(yīng)的移動(dòng)賦值重載函數(shù)就調(diào)用其對(duì)應(yīng)的移動(dòng)賦值重載函數(shù),如果沒(méi)有則調(diào)用拷貝賦值。

3.如果你提供了移動(dòng)賦值構(gòu)造或者移動(dòng)賦值重載函數(shù),那么編譯器就不會(huì)自動(dòng)生成。

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

1.萬(wàn)能引用

在模板中,&&表示的不是右值引用,而是萬(wàn)能引用,即既可以接收左值,又可以接收右值。

void PerfectForward(T&& t)
{
	Fun(forward<T>(t));
}

此時(shí)傳入的t既可以是左值,也可以是右值。

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

運(yùn)行以下程序,發(fā)現(xiàn)最終識(shí)別的都是左值引用。

void Func(int&& x)
{
	cout << "rvalue" << endl;
}
void Func(int& x)
{
	cout << "lvalue" << endl;
}
template<class T>
void PerfectForward(T&& t)
{
	Func(t);
}
int main()
{
	PerfectForward(10);//左值
	int a;
	PerfectForward(a);//左值
	PerfectForward(move(a));//左值
}	

這是因?yàn)橛抑狄靡坏┮昧?,就變成了左值,如果我們還希望保持該右值引用的特性的話,需要使用forward函數(shù)來(lái)對(duì)其進(jìn)行封裝:

	Func(forward<T>(t));

forward(t)來(lái)進(jìn)行封裝的意義在于,保持t原來(lái)的屬性,如果它原來(lái)是左值那么封裝之后還是左值,如果它是右值的引用,則將其還原成右值。該函數(shù)的作用稱為完美轉(zhuǎn)發(fā),由于這一性質(zhì),STL容器的插入也可以使用右值引用來(lái)實(shí)現(xiàn)。

即支持:

vector<int> v;v.push_back(111);

在該右值引用版本的插入中,調(diào)用的就是forward(val)。

到此這篇關(guān)于C++11新特性之右值引用與完美轉(zhuǎn)發(fā)詳解的文章就介紹到這了,更多相關(guān)C++右值引用 完美轉(zhuǎn)發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解C語(yǔ)言中的memset()函數(shù)

    詳解C語(yǔ)言中的memset()函數(shù)

    這篇文章主要介紹了C語(yǔ)言中的memset()函數(shù),包括其與memcpy()函數(shù)的區(qū)別,需要的朋友可以參考下
    2015-08-08
  • C++使用一個(gè)棧實(shí)現(xiàn)另一個(gè)棧的排序算法示例

    C++使用一個(gè)棧實(shí)現(xiàn)另一個(gè)棧的排序算法示例

    這篇文章主要介紹了C++使用一個(gè)棧實(shí)現(xiàn)另一個(gè)棧的排序算法,結(jié)合實(shí)例形式分析了C++借助輔助棧實(shí)現(xiàn)棧排序算法的相關(guān)操作技巧,需要的朋友可以參考下
    2017-05-05
  • c語(yǔ)言實(shí)現(xiàn)可自定義的游戲地圖

    c語(yǔ)言實(shí)現(xiàn)可自定義的游戲地圖

    這篇文章主要為大家詳細(xì)介紹了c語(yǔ)言實(shí)現(xiàn)可自定義的游戲地圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C++實(shí)現(xiàn)聊天程序

    C++實(shí)現(xiàn)聊天程序

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)類似QQ聊天程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • C語(yǔ)言分治法實(shí)現(xiàn)歸并排序

    C語(yǔ)言分治法實(shí)現(xiàn)歸并排序

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)歸并排序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • 深入了解C語(yǔ)言中常見(jiàn)的文件操作方法

    深入了解C語(yǔ)言中常見(jiàn)的文件操作方法

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言中常見(jiàn)的文件操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C語(yǔ)言中的初階指針詳解

    C語(yǔ)言中的初階指針詳解

    這篇文章主要介紹了C語(yǔ)言中的初階指針,介紹了其相關(guān)概念,具有一定參考價(jià)值。需要的朋友可以了解下,希望能夠給你帶來(lái)幫助
    2021-10-10
  • C語(yǔ)言最大公約數(shù)示例教程

    C語(yǔ)言最大公約數(shù)示例教程

    這篇文章主要為大家介紹了C語(yǔ)言最大公約數(shù)的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2021-11-11
  • C++用一棵紅黑樹(shù)同時(shí)封裝出set與map的實(shí)現(xiàn)代碼

    C++用一棵紅黑樹(shù)同時(shí)封裝出set與map的實(shí)現(xiàn)代碼

    set中存儲(chǔ)的一般為鍵K即可,而map存儲(chǔ)的一般都是鍵值對(duì)KV,也就是說(shuō)他們結(jié)構(gòu)是不同的,那么我們?nèi)绾尾拍苡靡活w紅黑樹(shù)同時(shí)封裝出set與map兩種容器呢,那么接下來(lái)我們具體地來(lái)研究下STL庫(kù)中是怎樣實(shí)現(xiàn)的,并且進(jìn)行模擬實(shí)現(xiàn),需要的朋友可以參考下
    2024-03-03
  • C語(yǔ)言 設(shè)計(jì)模式之訪問(wèn)者模式

    C語(yǔ)言 設(shè)計(jì)模式之訪問(wèn)者模式

    這篇文章主要介紹了C語(yǔ)言 設(shè)計(jì)模式之訪問(wèn)者模式的相關(guān)資料,需要的朋友可以參考下
    2017-01-01

最新評(píng)論