一、左值、右值、左值引用&、右值引用&&
main.cpp
#include <iostream>
using namespace std;
int main()
{
string str = "I love china";
const char *p = str.c_str();
cout << (void*)p << endl;
string &str1 = str;//str爲左值, str1爲左值引用
string &&str2 = "I love china";//"I love china"爲右值,str2爲右值引用
string str3 = std::move(str);//str左值轉右值,只有轉右值纔會調用移動構造函數,否則調用的是拷貝構造函數。
//調用了string的移動構造函數,原str爲空,值轉移到str3了
const char *p1 = str3.c_str();
cout << (void*)p1 << endl;//p和p1不一致,說明並不是名副其實的移動
string str4 = "I love china";
string && str5 = std::move(str4);//不會調用移動構造函數,只是個左值轉右值
str5 = "abc";//str5和str4同步修改
str4 = "edf";//str5和str4同步修改
int i = 0;
int& i1 = i;//i是左值,i1是左值引用
int&& i2 = 1;//1是右值,i2是右值引用
int && i3 = std::move(i);//左值轉右值,賦值給右值引用*/
}
左值:可以被賦值的,用左值引用&
右值:不能被賦值的,臨時對象,用右值引用&&
二、移動構造函數、移動賦值運算符
#include <iostream>
using namespace std;
class B
{
public:
B(): m_bm(100)
{
}
B(const B& tmp): m_bm(tmp.m_bm)
{
}
virtual ~B()
{
}
int m_bm;
};
class A
{
public:
A() :m_pb(new B()), kk(34)
{
cout << "類A的構造函數執行了" << endl;
}
A(const A& tmpa):m_pb(new B(*(tmpa.m_pb))),kk(tmpa.kk)//m_pb指向新的B對象
{
cout << "類A的拷貝構造函數執行了" << endl;
}
A(A&& tmpa):m_pb(tmpa.m_pb), kk(tmpa.kk)//m_pb指向舊的B對象
{
tmpa.m_pb = nullptr;
cout << "類A的移動構造執行了" << endl;
}
A &operator=(A && src)
{
if (this == &src)
return *this;
delete m_pb;
m_pb = src.m_pb;
kk = src.kk;
src.m_pb = nullptr;
cout << "類A的移動賦值運算符執行了" << endl;
return *this;
}
virtual ~A()
{
delete m_pb;
cout << "類A的析構函數執行了" << endl;
}
private:
B* m_pb;
int kk;
};
static A getA()
{
A a;//臨時對象爲右值
return a;
}
int main()
{
A a = getA();
}
輸出:
類A的構造函數執行了
類A的移動構造執行了(臨時對象是右值,如果沒有移動構造函數,那隻能拷貝構造函數了)
類A的析構函數執行了
類A的析構函數執行了
int main()
{
A a;
A a1(std::move(a));//左值轉右值後調用移動構造函數
}
輸出:
類A的構造函數執行了
類A的移動構造執行了
類A的析構函數執行了
類A的析構函數執行了
a和a1的成員變量m_pb和kk還是重新分配的,只不過m_pb指向的內存直接用原來的了,原來m_pb爲NULL。
int main()
{
A a;
A a2;
a2 = std::move(a);//移動賦值運算符
}
輸出:
類A的構造函數執行了
類A的構造函數執行了
類A的移動賦值運算符執行了
類A的析構函數執行了
類A的析構函數執行了
總結:
有自己的拷貝構造函數、拷貝賦值運算符、或者析構函數,編譯器就不會爲我們生成默認的移動構造函數、移動賦值運算符
如果不是上面的情況,滿足如下條件,編譯器會爲我們生成默認的移動構造函數、移動賦值運算符
1、成員變量可移動(右值)
2、成員變量爲對象,對象可移動(有移動構造函數或者本身可移動)
爲什麼要引入右值和右值引用的概念呢?就是爲了編寫移動構造函數,編寫移動構造函數就是爲了提高效率。