C++11中令人吐血的"移動語義"和"新的右值引用"
本文轉載自:https://blog.csdn.net/xuwqiang1994/article/details/79924310
1.概述
沒有C++11之前我都這樣寫程式碼的
Class* ptr = new Class(xxx);
感覺指標用的挺好的,邏輯清晰,很容易理解。
C++11出來之後,有了"移動語義"和"新的右值引用"的概念,可以預見以後看程式碼有多頭疼。
2.左值與右值
左值(lvalue):代表一個在記憶體中佔有確定位置的物件;
右值(rvalue):通過排他性來定義,不是lvalue的都屬於rvalue,即在不在記憶體中佔有確定位置的表示式;
2.1 例1:
int var;
var = 4;
4 = var; //ERROR!
(var + 10) = 4; //ERROR!
如上, 顯然"4、(var + 10)"這些表示式是不能作為左值的。
2.2 例2:
int foo() { return 2; }
int main()
{
foo() = 2; //ERROR
return 0;
}
這裡的foo()也不能作為左值,而C++中的引用(reference)讓這成為可能
2.3 例3:
int globalvar = 20; int& foo() { return globalvar; } int main() { foo() = 10; return 0; }
這裡的"&"當表示引用含義的時候,修飾的需要是一個左值。
3.不用指標勝似指標的做法
3.1 疑問
拿C++11舉例,C++11裡引入了thread,下面的程式碼會不會讓你感到疑惑?
std::thread threads[5];
for (int i=0; i<5; ++i)
threads[i] = std::thread(pause_thread,i+1);
請問thread的建構函式到底被呼叫了幾次呢?
3.2 樣例1
我們不考慮thread,看下面這個Intvec程式碼樣例:
class Intvec { public: explicit Intvec(size_t num = 0) : m_size(num), m_data(new int[m_size]) { log("constructor"); } ~Intvec() { log("destructor"); if (m_data) { delete[] m_data; m_data = 0; } } Intvec(const Intvec& other) : m_size(other.m_size), m_data(new int[m_size]) { log("copy constructor"); for (size_t i = 0; i < m_size; ++i) m_data[i] = other.m_data[i]; } Intvec& operator=(const Intvec& other) { log("copy assignment operator"); Intvec tmp(other); std::swap(m_size, tmp.m_size); std::swap(m_data, tmp.m_data); return *this; } private: void log(const char* msg) { cout << "[" << this << "] " << msg << "\n"; } size_t m_size; int* m_data; }; int main() { Intvec v2; cout << "assigning lvalue...\n"; v2 = Intvec(20); cout << "ended assigning lvalue...\n"; return 0; }
執行結果
$ g++ main2.cpp -o main2 --std=c++11
$ ./main2
[0x7ffe204edc50] constructor
assigning lvalue...
[0x7ffe204edc60] constructor
[0x7ffe204edc50] copy assignment operator
[0x7ffe204edc20] copy constructor
[0x7ffe204edc20] destructor
[0x7ffe204edc60] destructor
ended assigning lvalue...
[0x7ffe204edc50] destructor
結果呼叫3次建構函式:
Intvec v2;
Intvec(20);
Intvec tmp(other);
3.3 樣例2:
我們再加一個成員函式;(不用刪除原來的operator=)
Intvec& operator=(Intvec&& other)
{
log("move assignment operator");
std::swap(m_size, other.m_size);
std::swap(m_data, other.m_data);
return *this;
}
執行結果:
$ g++ main2.cpp -o main2 --std=c++11
$ ./main2
[0x7ffe5aa0ad70] constructor
assigning lvalue...
[0x7ffe5aa0ad80] constructor
[0x7ffe5aa0ad70] move assignment operator
[0x7ffe5aa0ad80] destructor
ended assigning lvalue...
[0x7ffe5aa0ad70] destructor
結果只調用了兩次建構函式。
從外觀上看
Intvec& operator=(Intvec&& other)和
Intvec& operator=(const Intvec& other)
在傳參上並沒有什麼不同。但顯然編譯器知道自己該呼叫哪個函式。
4.總結
"&&"就是C++11支援的"新右值引用操作符",operator=(Intvec&& other)這個函式就是實現"移動語義"的一種方法。
PS:C++越改越像個指令碼語言,圖啥?
從個人角度看,以後寫程式碼,我還是傾向於使用
Intvec* p2 = new Intvec(20);
delete p2;
的方式,這隻呼叫一次建構函式,而且邏輯還很清晰,不用考慮類內部的實現。
Intvec v2 = Intvec(20); //也只調用一次,這就是另外一回事了。
本文轉載自:https://blog.csdn.net/xuwqiang1994/article/details/79924310