溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++模板編程特性之移動語義實例分析

發(fā)布時間:2022-08-23 16:31:53 來源:億速云 閱讀:151 作者:iii 欄目:開發(fā)技術

這篇文章主要講解了“C++模板編程特性之移動語義實例分析”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C++模板編程特性之移動語義實例分析”吧!

C++的值類型

我們知道,每個變量都有類型,或整形或字符型等來進行了分類,不僅如此,C++表達式(帶有操作數(shù)的操作符、字面量、變量名等)在類型的屬性上,還有一種屬性,即值類別(value category)。且每個表達式只屬于三種基本值尖別中的一種:左值(lvalue),右值(rvalue),將亡值(xvalue),每個值類別都與某種引用類型對應。

C++模板編程特性之移動語義實例分析

其中,左值和將亡值成為泛左值(generalized value,gvalue),純右值和將亡值合稱為右值(right value,rvalue)。

一般我們講,左值就是可以取地址的,具有名字的,比如 int a; a是變量的名字,&a是變量的地址,a就是左值。那么右值呢,自然就是不可以取地址的,比如int b=10; 而這個10就是一個右值,在內(nèi)存中不會分配有地址,自然也不能取地址。

將亡值,則是指在調(diào)用某個函數(shù)退出返回時,如果函數(shù)有返回值,那么就會有將亡值的存在,為什么稱之為將亡值,就是說這個值在函數(shù)作用域創(chuàng)建,但由于函數(shù)返回結束,局部變量都會銷毀,故會產(chǎn)生一個將亡值來接收這個值,完成賦值的任務。

從上圖也可以看出,將亡值既可能轉(zhuǎn)為左值,也可能成為右值,那么關鍵就在于要看是否具有名字了。

下面看這樣一段程序:

#include<iostream>
#include<type_traits>
using namespace std;
class MyString
{
private:
	char* str; // heap;
public:
	MyString(const char* p = nullptr) :str(nullptr)
	{
		if (p != nullptr)
		{
			int n = strlen(p) + 1;
			str = new char[n];
			strcpy_s(str, n, p);
		}
		cout << "Create MyString: " << this << endl;
	}
	MyString(const MyString& st)
	{
		if(st.str!=NULL)
		str = st.str;
		cout << "Copy Create MyString: " << this << endl;
	}
	MyString& operator=(const MyString& st)
	{
		if (st.str != NULL)
		str = st.str;
		cout << this << " operator=(const MyString &):  " << &st << endl;
		return *this;
	}
	~MyString()
	{
		delete[]str;
		str = nullptr;
		cout << "Destroy MyString : " << this << endl;
	}
	void PrintString() const
	{
		if (str != nullptr)
		{
			cout << str << endl;
		}
	}
};
int main()
{
	MyString *a=new MyString("lisa");
	MyString *b = a;
	delete b;
	a->PrintString();
	return 0;
}

MyString類型成員有指針變量,且采用淺拷貝方式。當程序運行時,可以看到,兩個指針指向了同一個地址,此時,若釋放了b指針,再以a指針訪問指針成員,就會出現(xiàn)問題。

C++模板編程特性之移動語義實例分析

還有,當函數(shù)以值類型返回,構造臨時對象,若有指針變量,且采用淺拷貝,就會出現(xiàn)多次析構的問題,導致程序崩潰。

當我們將程序都改為深拷貝時,深拷貝又會導致,程序多次騷擾對空間,此時就提出了move語義。

std::move

std::move其實并沒有移動任何東西,它唯一的功能是將一個左值強制轉(zhuǎn)化為右值引用,繼而可以通過右值引用使用該值,以用于移動語義。從實現(xiàn)上講,move基本等同于一個類型轉(zhuǎn)換。

值得注意的是,通過move轉(zhuǎn)化成右值后,被轉(zhuǎn)化的左值的生命周期并沒有隨著左右值的轉(zhuǎn)化而改變。但通常情況下,我們需要轉(zhuǎn)換成右值引用的還是一個確定生命期即將結束的對象。

右值引用與移動構造和移動賦值

在c++11中增加了右值引用的概念,即對右值的引用,通過右值引用,可以延長右值的生命期。我們都知道左值引用是變量值的別名,那么右值引用則是不具名變量的別名。

右值引用是不能綁定到任何左值的,但有個例外,常量左值是一個萬能引用,可以引用任何值,包括右值引用。

class MyString
{
private:
	char* str; // heap;
public:
	MyString(const char* p = nullptr) :str(nullptr)
	{
		if (p != nullptr)
		{
			int n = strlen(p) + 1;
			str = new char[n];
			strcpy_s(str, n, p);
		}
		cout << "Create MyString: " << this << endl;
	}
	MyString(const MyString& st)
	{
		if (st.str != nullptr)
		{
			int n = strlen(st.str) + 1;
			str = new char[n];
			strcpy_s(str, n, st.str);
		}
		cout << "Copy Create MyString: " << this << endl;
	}
	MyString& operator=(const MyString& st)
	{
		if (this != &st && str != st.str)
		{
			delete[]str;
			if (st.str != nullptr)
			{
				int n = strlen(st.str) + 1;
				str = new char[n];
				strcpy_s(str, n, st.str);
			}
		}
		cout << this << " operator=(const MyString &):  " << &st << endl;
		return *this;
	}
	MyString(MyString&& st)
	{
		str = st.str;
		st.str = nullptr;
		cout << "Move Copy Create MyString" << this << endl;
	}
	MyString& operator=(MyString&& st)
	{
		if (this == &st) return *this;
		if (this->str == st.str)
		{
			st.str = nullptr;
			return *this;
		}
		delete[]str;
		str = st.str;
		st.str = nullptr;
		cout << "Move operator=(MyString &&)" << endl;
		return *this;
	}
	~MyString()
	{
		delete[]str;
		str = nullptr;
		cout << "Destroy MyString : " << this << endl;
	}
	void PrintString() const
	{
		if (str != nullptr)
		{
			cout << str << endl;
		}
	}
};
int main()
{
	const MyString stra("hello");
	MyString strb;
	strb = std::move(stra);//調(diào)用普通的賦值方法
	strb.PrintString();
	return 0;
}

C++模板編程特性之移動語義實例分析

這里的move還是調(diào)用普通的賦值函數(shù),并未做到真正的資源轉(zhuǎn)移,但是若寫成如下結構:

int main()
{
	const MyString stra("hello");
	MyString strb;
	//strb = std::move(stra);//調(diào)用普通的賦值方法
	strb = (MyString&&)stra;
	strb.PrintString();
	return 0;
}

C++模板編程特性之移動語義實例分析

通過右值引用,可以延長右值的生命期。從而,有了右值引用出現(xiàn),這個時候配合移動構造與移動賦值,就可以完成資源轉(zhuǎn)移了。

然后,我們再看一個例子:

MyString& fun()
{
	MyString st=("newdata");
	return st;//xvalue
}
int main()
{
	MyString("zhangsan").PrintString();
	const MyString& a = fun();
	a.PrintString();
	MyString& b = fun();
	b.PrintString();
	return 0;
}

在程序運行時,會發(fā)現(xiàn)程序崩潰了,原因是:

C++模板編程特性之移動語義實例分析

函數(shù)中返回局部對象的引用,因為函數(shù)調(diào)用結束會銷毀局部對象,而引用則就成為了非法的訪問。因為不要在函數(shù)中返回局部對象的引用。

若我們將fun()函數(shù)的返回改為右值引用呢?

MyString&& fun()
{
	return MyString("newdata");
}
int main()
{
	MyString("zhangsan").PrintString();
	const MyString& a = fun();//x
	a.PrintString();
	//MyString& b = fun();
	//b.PrintString();
	MyString&& c = fun();//x
	c.PrintString();
	MyString&& d = c;//error
	return 0;
}

將亡值回去的時候,就得看看有沒有具名,一旦具名就是左值了,否則是右值

C++模板編程特性之移動語義實例分析

可以發(fā)現(xiàn),右值引用是不具名的,但是右值引用本身卻是個左值,經(jīng)過右值引用b接收后,就已經(jīng)變成了左值,具有了名字。

感謝各位的閱讀,以上就是“C++模板編程特性之移動語義實例分析”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對C++模板編程特性之移動語義實例分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

c++
AI