c++的左值(lvalue),右值(rvalue),移動語意(move),完美轉發(forward)
c++的左值,右值 精辟總結
當一個物件被用作右值的時候,使用的是物件的值(內容);當物件被用作左值的時候,用的是物件的身份(在記憶體中的位置)左值右值,完美轉發參考檔案,
左值持久,右值短暫;move:顯示地將一個左值轉換為對應右值的參考型別,還可以獲取系結到左值上的右值參考,int&& rr3 = std::move(rrl); 使用move就意味著除了對rrl賦值或銷毀它外,我們不再使用它,
std::forward
移動語意解決了無用拷貝的問題:移動建構式;
右值參考:函式的回傳值,
int& 左值參考
int&& 右值參考
c++中無用拷貝的情況
/*類里面 沒有移動建構式
這樣就會使用 copy construct function,會導致大量無用的 memory copy,
*/
class Test {
public:
string desc;
int * arr{nullptr};
Test():arr(new int[5000]{1,2,3,4}) {
cout << "default constructor" << endl;
}
Test(const Test & t) {
cout << "copy constructor" << endl;
if (arr == nullptr) arr = new int[5000];
copy(t.arr,t.arr+5000, arr);
}
~Test(){
cout << "destructor " << desc << endl;
delete [] arr;
}
};
Test createTest() {
return Test();
}
int main(){
Test reusable;
reusable.desc = "reusable";
Test duplicated(reusable);
duplicated.desc = "duplicated";
Test t(createTest());
t.desc = "t";
cout<<"end"<<endl;
}
運行結果
default constructor
copy constructor
default constructor
end
destructor t
destructor duplicated
destructor reusable
使用移動語意避免無用的拷貝
/*使用移動 construct function,避免無用的memory copy,
*/
class Test {
public:
string desc;
int * arr{nullptr};
Test():arr(new int[5000]{1,2,3,4}) {
cout << "__default constructor" << endl;
}
Test(const Test & t) {
cout << "__copy constructor" << endl;
if (arr == nullptr) arr = new int[5000]; //在這里要將 t.arr 置為空,因為經過move之后,我們認為不在使用這個值了,避免在新的物件中把指標釋放后,原來的物件中存在野指標的現象
copy(t.arr,t.arr+5000, arr);
}
Test(Test && t): arr(t.arr) {
cout << "__move constructor" << endl;
t.arr = nullptr;
}
~Test(){
cout << "..destructor " << desc << endl;
delete [] arr;
}
};
Test createTest(string str) {
Test rt;
rt.desc = str;
cout<<"createTest:"<<&rt<<endl;
return rt;
}
void main(){
Test reusable;
reusable.desc = "reusable";
cout<<"reusable.arr "<<reusable.arr<<endl;
Test duplicated(std::move(reusable));
duplicated.desc = "duplicated";
cout<<"reusable.arr "<<reusable.arr<<endl;
cout<<"duplicated.arr "<<duplicated.arr<<endl;
cout<<"rvalue--"<<endl;
Test&& rt1 = createTest("rval"); //使用右值參考接收
cout<<"rt1.arr "<<rt1.arr<<endl;
cout<<"no rvalue--"<<endl;
Test rt2 = createTest("normalVal"); //不使用右值參考接收,可以看到這里比使用右值參考接收 多了一次構造和析構(createTest中的臨時物件)
cout<<"createTest:"<<&rt2<<endl; //尷尬,其實這里編譯器已經做了優化了,可以看到第地址一樣
cout<<"rt2.arr "<<rt2.arr<<endl;
cout<<"end"<<endl;
}
輸出結果
__default constructor
reusable.arr 0x56521b946e70
__move constructor
reusable.arr 0
duplicated.arr 0x56521b946e70
rvalue--
__default constructor
createTest:0x7ffd092ea390
rt1.arr 0x56521b94c0b0
no rvalue--
__default constructor
createTest:0x7ffd092ea3c0
createTest:0x7ffd092ea3c0
rt2.arr 0x56521b950ee0
end
..destructor normalVal
..destructor rval
..destructor duplicated
..destructor reusable
左值參考右值參考
//左值參考和右值參考
void foo(const int & i) { cout << "const int & " << i << endl; }
void foo(int & i) { cout << "int & " << i << endl; }
void foo(int && i) { cout << "int && " << i << endl; }
void foo(const int && i) { cout << "const int && " << i << endl; }
void main(){
int i = 2;
foo(i);
foo(2);
foo([]()->const int && {return 2;}());
}
完美轉發
/*在main當中呼叫relay,Test的臨時物件作為一個右值傳入relay,在relay當中又被轉發給了func,那這時候轉發
給func的引數t也應當是一個右值,也就是說,我們希望:當relay的引數是右值的時候,func的引數也是右值;當
relay的引數是左值的時候,func的引數也是左值,
*/
class Test {
public:
int * arr{nullptr};
Test():arr(new int[5000]{1,2,3,4}) {
cout << "default constructor" << endl;
}
Test(const Test & t) {
cout << "copy constructor" << endl;
if (arr == nullptr) arr = new int[5000];
copy(t.arr,t.arr+5000, arr);
}
Test(Test && t): arr(t.arr) {
cout << "move constructor" << endl;
t.arr = nullptr;
}
~Test(){
cout << "destructor" << endl;
delete [] arr;
}
};
template <typename T>
void func(T t) {
cout << "in func" << endl;
}
template <typename T>
void relay(T&& t) {
cout << "in relay" << endl;
func(t);
}
//完美轉發
template <typename T>
void relay1(T&& t) {
cout << "in relay " << endl;
func(std::forward<T>(t));
}
void main() {
// relay(Test());
// cout<<"end"<<endl;
relay1(Test());
cout<<"end"<<endl;
}
更多編程資料見公眾號 xutopia77
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/412825.html
標籤:C++
下一篇:返回列表