C++移動語義與右值引用完美展示與原理介紹 更新於2018-05-01
C++移動語義
移動語義顧名思義是記憶體資料的移動,實際就是記憶體資料所有權的轉移。
目的:
1 函式可以直接返回值型別的物件而不發生物件拷貝
2 除了函式返回值,自定義型別的普通賦值也可以走移動語義,目的相同:減少記憶體開闢和釋放(RapidJson庫就是這種情況)
例如:vector<int> fun(void);
由於vector的移動建構函式會將fun內部返回的vector所擁有的記憶體直接通過swap的方式跟返回值物件交換。所以沒有發生重新構造一個vector<int>並重新開闢記憶體再賦值這個過程。從而移動就等於偷樑換柱了一樣,當然,要求移動之後,源物件要能夠正常析構,且成員資料是未知的(已經被轉移走了,只剩下空指標了)
直接上程式碼:
以自定義類MyString來演示建立多個物件卻不用開闢新記憶體,也不用拷貝記憶體:
下面的getString函式內部,建立了三個物件,但是它們的資源卻在像接力棒一樣的傳給下一個。從而不用開闢新記憶體,也不用拷貝內容。
最後一個物件用於真正的列印工作。
原理:
T&& std::move(const T& t)
- std::move用於返回物件的右值引用
- 而MyString的建構函式接受一個右值引用物件
- MyString的建構函式在得到右值引用物件後交換資源,從而實現了偷樑換柱。避免了記憶體的重新開闢、拷貝、釋放的過程。
總結:
從本例程式碼來看,右值引用相當於在引數傳遞的方式上增加了一種傳參的形式。
原有的傳參形式:
- 傳地址:T* C語言用法
- 傳引用:T& 常用於修改物件
- 傳應用:const T& 常用於只讀物件
- 傳值: T 常用於小型物件非頻繁傳值
- 傳右值引用: T&& 常用於臨時物件的資源交換,複雜物件避免拷貝
//// move example
#include <utility> // std::move
#include <iostream> // std::cout
#include <vector> // std::vector
#include <string> // std::string
using namespace std;
class MyString
{
friend ostream& operator<<(ostream& os, const MyString& str)
{
return os<<str.m_pData;
}
public:
MyString(void)
:m_pData(nullptr),m_length(0),m_id(++s_i)
{
cout<<"MyString("<<m_id<<")"<<endl;
}
~MyString()
{
cout<<"~MyString("<<m_id<<")"<<endl;
Clear();
}
MyString(const char* _pData, int _length)
:m_pData(nullptr),m_length(0),m_id(++s_i)
{
cout<<"MyString(const char*,int,"<<m_id<<")"<<endl;
Copy(_pData, _length);
}
MyString(const MyString& _strFrom)
:m_pData(nullptr),m_length(0),m_id(++s_i)
{
cout<<"MyString("<<m_id<<", const MyString& "<<_strFrom.m_id<<" )"<<endl;
if (_strFrom.m_pData == m_pData)
{
//do nothing
}
else
{
Copy(_strFrom.m_pData, _strFrom.m_length);
}
}
//使用右值引用來建立物件時,使用資源交換來避免記憶體拷貝和重新建立
MyString(MyString&& _strFrom)
:m_pData(nullptr),m_length(0),m_id(++s_i)
{
cout<<"MyString("<<m_id<<", MyString&& "<<_strFrom.m_id<<" )"<<endl;
if (_strFrom.m_pData == m_pData)
{
//do nothing
}
else
{
swap(m_pData,_strFrom.m_pData);
swap(m_length,_strFrom.m_length);
}
}
protected:
void Clear(void)
{
if (nullptr != m_pData)
{
delete[] m_pData;
m_pData = nullptr;
m_length = 0;
}
}
void Copy(const char* _pData, int _length)
{
Clear();
m_pData = new char[_length];
for (size_t i = 0; i< _length; ++i)
{
m_pData[i] = _pData[i];
}
m_length = _length;
}
private:
char* m_pData;
int m_length;
int m_id;
static int s_i;
};
int MyString::s_i = 0;
MyString getMyString(void)
{
//下面的程式碼建立了三個MyString物件,只發生了1次開闢記憶體,0次記憶體拷貝
MyString s("12345",6);//id == 1
MyString s1(move(s));//id == 2
return move(s1);//id == 3 這個被函式返回
//~MyString(2)
//~MyString(1)
}
int main () {
cout<<getMyString()<<endl;//列印的是函式返回的id == 3的那個物件
return 0;
}
相關文章
- 對C++11中的`移動語義`與`右值引用`的介紹與討論C++
- C++左值引用與右值引用C++
- c++ 左值引用與右值引用C++
- C++11 中的右值引用與轉移語義C++
- [c++11]我理解的右值引用、移動語義和完美轉發C++
- C++11 左值引用和右值引用與引用摺疊和完美轉發C++
- C++右值引用C++
- C++ 右值引用和左值引用C++
- C++11 新特性之右值引用與轉移語義C++
- C++11之右值引用、移動語義C++
- C++左值右值完美轉發轉移C++
- c++中的左值與右值C++
- C++ 11 中的右值引用C++
- C++智慧指標之shared_ptr與右值引用(詳細)C++指標
- 右值引用
- Google C++ Coding Style:右值引用(Rvalue Reference)GoC++
- JavaScript 左值與右值JavaScript
- c++11之左值引用和右值引用C++
- 值物件與引用物件物件
- C++霧中風景10:聊聊左值,純右值與將亡值C++
- C++移動建構函式以及move語句簡單介紹C++函式
- C筆記-左值與右值筆記
- C++11/14::右值引用C++
- FFT原理及C++與MATLAB混合程式設計詳細介紹FFTC++Matlab程式設計
- DAPP系統的原理與介紹APP
- C++ 左值和右值C++
- C++移動語義 詳細講解【Cherno C++教程】C++
- 一、程式語言簡介與C++C++
- C++11中的右值引用C++
- 新書介紹 -- 《Redis核心原理與實踐》新書Redis
- Python與C++引用分析PythonC++
- PostCSS 常用外掛與語法介紹CSS
- 值傳遞與引用傳遞
- 值型別與引用型別型別
- 跨境物流APP開發價值與作用介紹APP
- C++中指標與引用詳解C++指標
- C++中的指標與引用C++指標
- C++引用與指標的比較C++指標