c 右值引用

2021-10-17 14:12:39 字數 3716 閱讀 2029

右值引用若不作為函式引數使用,基本等於濫用

右值引用 (rvalue referene) 是 c++ 新標準 中引入的新特性 , 它實現了移動語義 (move sementics) 和完美** (perfect forwarding)。它的主要目的有兩個方面: 

1. 消除兩個物件互動時不必要的物件拷貝,節省運算儲存資源,提高效率。 

2. 能夠更簡潔明確地定義泛型函式。

何為右值:

c++( 包括 c) 中所有的表示式和變數要麼是左值,要麼是右值。通俗的左值的定義就是非臨時物件,

那些可以在多條語句中使用的物件。 所有的變數都滿足這個定義,在多條**中都可以使用,都是左值。

右值是指臨時的物件,它們只在當前的語句中有效。

int i = 0;// 在這條語句中,i 是左值,0 是臨時變數,就是右值
在c++11之前,右值是不能引的,如

int &a = 1;// error : 非常量的引用必須為左值

const int &a = 1;// 我們最多只能用常量引用來繫結乙個右值

在c++11中我們可以引用右值,使用&&實現:

int &&a = 1;
應用場景:

如下string類,實現了拷貝構造和賦值運算子過載

#include #include using namespace std;

class mystring

mystring(const char* s)

mystring(const mystring& str)

mystring& operator=(const mystring& str)

cout << "等號操作符過載" << str.m_data << endl;

return *this;

}~mystring()

}private:

void init_data(const char* s)

char* m_data;

size_t m_len;

};void test()

int main()

輸出:

建構函式hello

等號操作符過載hello

析構函式

建構函式world

拷貝建構函式world

析構函式

析構函式

析構函式

總共執行了2次拷貝,mystring("hello")和mystring("world")都是臨時物件,臨時物件被使用完之後會被立即析構,在析構函式中free掉申請的記憶體資源。

如果能夠直接使用臨時物件已經申請的資源,並在其析構函式中取消對資源的釋放,這樣既能節省資源,有能節省資源申請和釋放的時間。 這正是定義移動語義的目的。

通過加入定義移動建構函式和轉移賦值操作符過載來實現右值引用(即復用臨時物件):

#include #include using namespace std;

class mystring

mystring(const char* s)

mystring(mystring&& str)

mystring& operator=(mystring&& str)

return *this;

}~mystring()

}private:

void init_data(const char* s)

char* m_data;

size_t m_len;

};void test()

int main()

輸出:

建構函式hello

移動等號操作符過載hello

建構函式world

移動拷貝建構函式world

析構函式

析構函式

需要注意的是:右值引用並不能阻止編譯器在臨時物件使用完之後將其釋放掉的事實,

所以移動建構函式和移動賦值操作符過載函式 中都將_data賦值為了null,而且析構函式中保證了_data != null才會釋放。

標準庫函式std::move

既然編譯器只對右值引用才能呼叫移動建構函式和移動賦值函式,又因為所有命名物件都只能是左值引用。

在這樣的條件了,如果已知乙個命名物件不再被使用而想對它呼叫轉移建構函式和轉移賦值函式

也就是把乙個左值引用當做右值引用來使用,怎麼做呢?

標準庫提供了函式 std::move,這個函式以非常簡單的方式將左值引用轉換為右值引用。

#include using namespace std;

void processvalue(int& i)

void processvalue(int&& i)

int main()

輸出:

lvalue processed:1

rvalue processed:1

std::move在提高swap函式效能上有非常大的幫助,一般來說,swap函式的通用定義如下:

template void swap(t& a, t& b)

結合std::move 和 右值引用,可以避免不必要的拷貝。swap的定義變為:

#include #include using namespace std;

class mystring

mystring(const char* s)

mystring(mystring&& str)

mystring& operator=(mystring&& str)

return *this;

}~mystring()

}private:

void init_data(const char* s)

char* m_data;

size_t m_len;

};namespace myt

}void test()

int main()

精確傳遞(perfect forwarding)

精確傳遞就是在引數傳遞過程中,所有這些屬性和引數值都不能改變。在泛型函式中,這樣的需求非常普遍

forward_value函式只有乙個引數val,定義如下:

template void forward_value(const t& val)  

template void forward_value(t& val)

函式 forward_value 為每乙個引數必須過載兩種型別,t& 和 const t&,否則,下面四種不同型別引數的呼叫中就不能同時滿足:

int a = 0; 

const int &b = 1;

forward_value(a); // int&

forward_value(b); // const int&

forward_value(2); // int&

對於乙個引數就要過載兩次,也就是函式過載的次數和引數的個數是乙個正比的關係。這個函式的定義次數對於程式設計師來說,是非常低效的。我們看看右值引用如何幫助我們解決這個問題:

template void forward_value(t&& val)
只需要定義一次,接受乙個右值引用的引數,就能夠將所有的引數型別原封不動的傳遞給目標函式。

c 左值 右值 右值引用 左值引用

c 裡一切值必須屬於左值 右值兩者之一。左值 一切變數 包括用const修飾的變數 物件 包括引用都屬於左值 右值 一切字面值 可以是巨集 臨時無名物件 函式返回值 表示式 如a n 說明一下 函式返回值,返回的是某乙個型別的值,並不是返回變數。左值並不是說能放在 左邊的值就是左值 雖然用const...

c 左值 右值 左值引用 右值引用

在c語言中,左值認為是賦值語句的左側,右值認為是賦值語句的右側。在c 中,意義稍有不同。c 中,每乙個表示式會產生乙個左值或者右值,相應的,該表示式也就被稱作 左值表示式 右值表示式 乙個左值表示式的求值結果是乙個物件或者是乙個函式。左值可以當右值使用,而右值不能當左值使用。c prime 中這麼簡...

C 左值 右值 左值引用 右值引用

就變數而言,對於一些變數,我們只會讀取並使用它們的值,而不會改變他們的值 唯讀 對於其餘的變數,我們既會讀取它們的值,有的時候還會改變它們的值 讀寫 這是很常見的。在c 中,前一種變數稱為右值,後一種變數稱為左值,例如 int a 1 a是左值,1是右值稍稍不同的一點是,在c 中,乙個變數是左值還是...