本文轉載自: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