C++ push_back()左值和右值的區(qū)別是什么?
首先理解下左值和右值。
C++98/03標準中:左值是指有名字的對象,右值是指臨時對象,但是我們提到的時候并不多。
C++11標準對左值和右值做了更細致的劃分:
左值 (Lvalue):表示內存中的一個具體位置,可以取地址,通常是一個對象或者變量的引用。
右值 (Rvalue):臨時的、不可取地址的對象,通常是表達式的結果。
將亡值(xvalue, expiring value):一種特殊的右值,表示將要銷毀的對象(如std::move()返回的對象)
int&& r = std::move(x); // std::move(x) 是將亡值
純右值(prvalue, pure rvalue):臨時值,表示一個臨時的對象或常量,如字面量、函數(shù)返回值等。
int&& r = 10; // 10 是純右值(臨時對象)
類左值:類左值是一個統(tǒng)稱,涵蓋了左值(lvalue)和將亡值(xvalue)。它們表示可以引用的對象。類左值包括了所有能夠通過&或&&引用的值,無論是左值還是將亡值。
int x = 10;
int& r = x; // x 是類左值(glvalue)
int&& r2 = std::move(x); // std::move(x) 是將亡值(xvalue)
push_back()是std::vector容器的一個成員函數(shù),用來將一個元素添加到容器的末尾?!?/span>
C++11之前,push_back()只有接受const左值引用的版本,所以無論是左值還是右值都會被拷貝到vector中。但C++11引入了移動語義,這時候有了右值引用的重載版本。
如果有std::string str = "hello";
然后v.push_back(str),這時候str是左值,會被拷貝。
而如果是v.push_back(std::move(str)),或者直接push_back("hello"),這時候就是右值,觸發(fā)移動構造,原str的內容被移動到vector中,之后str就空了。
左值傳遞:
當調用push_back(左值)時,會調用拷貝構造函數(shù)。
#include <iostream>
#include <vector>
class MyClass {
public:
MyClass(int value) : value(value) {
std::cout << "MyClass(" << value << ") constructor\n";
}
// 拷貝構造函數(shù)
MyClass(const MyClass& other) : value(other.value) {
std::cout << "MyClass(" << value << ") copy constructor\n";
}
private:
int value;
};
int main() {
std::vector<MyClass> vec;
MyClass obj(10);
// 左值傳遞
vec.push_back(obj); // 會調用拷貝構造函數(shù)
return 0;
}
輸出:
MyClass(10) constructor
MyClass(10) copy constructor
在這個例子中,當我們將obj(一個左值)傳遞給push_back()時,std::vector需要復制這個對象,調用了MyClass的拷貝構造函數(shù)?!?/span>
右值傳遞:
當調用push_back(右值)時,會調用移動構造函數(shù)。
容器可以“移動”這個對象到內部,而不需要進行復制。
這意味著會調用對象的移動構造函數(shù)(如果存在的話),更高效,尤其是當對象比較大或包含大量數(shù)據(jù)時。
#include <iostream>
#include <vector>
class MyClass {
public:
MyClass(int value) : value(value) {
std::cout << "MyClass(" << value << ") constructor\n";
}
// 移動構造函數(shù)
MyClass(MyClass&& other) noexcept : value(other.value) {
std::cout << "MyClass(" << value << ") move constructor\n";
}
private:
int value;
};
int main() {
std::vector<MyClass> vec;
// 右值傳遞
vec.push_back(MyClass(20)); // 會調用移動構造函數(shù)
return 0;
}
輸出:
MyClass(20) constructor
MyClass(20) move constructor
在這個例子中,通過MyClass(20)創(chuàng)建了一個臨時對象,這是一個右值。當它傳遞給push_back()時,std::vector會調用移動構造函數(shù),而不是拷貝構造函數(shù)。這樣,MyClass(20)的資源會被“移動”到vec中,而不需要額外的內存分配和復制數(shù)據(jù)?!?/span>
底層實現(xiàn)原理:
std::vector 的 push_back() 提供兩個重載版本:
void push_back(const T& val); // 左值版本:拷貝
void push_back(T&& val); // 右值版本:移動(C++11 新增)
vector的push_back有兩個重載版本:
一個是const T&,另一個是T&&。
當傳入左值時,編譯器選擇第一個版本,進行拷貝;
當傳入右值時,選擇第二個版本,進行移動?!?/span>
總結:
特性 | 左值(push_back(a)) | 右值(push_back(std::move(a))) |
拷貝/移動 | 拷貝構造 | 移動構造 |
原對象狀態(tài) | 保留原值 | 有效但未定義(通常為空) |
性能 | 可能較慢(深拷貝) | 通常更快(僅僅轉移資源) |
適用對象 | 需要保留的具名對象 | 臨時對象或者不再需要的對象 |