欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++?左值、右值、左值引用、右值引用的用途及區(qū)別

 更新時間:2025年09月20日 09:07:11   作者:xclic  
本文給大家介紹了C++左值、右值、左值引用、右值引用用途及區(qū)別,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧

1、左值與右值

左值和右值是表達式的屬性,核心區(qū)別在于:能否取地址、是否有持久的存儲。

1.1 左值:有名字、能取地址、可被修改(通常)

左值是 “可以放在賦值號左邊” 的表達式(但并非絕對,如 const 左值不能被修改),它有明確的內存地址,生命周期較長(如變量)。

int a = 10; // a 是左值(有名字、能取地址)
int* p = &a; // 合法:左值可以取地址
const int b = 20; // b 是 const 左值(有名字、能取地址,但不能修改)
// b = 30; // 錯誤:const 左值不可修改
int arr[5]; 
arr[0] = 1; // arr[0] 是左值(數組元素有地址)

1.2 右值:無名字、不能取地址、臨時存在

右值是 “只能放在賦值號右邊” 的表達式,通常是臨時結果(如字面量、表達式計算結果),沒有持久的內存地址,生命周期短暫(表達式結束后銷毀)。

int a = 10;
int b = 20;
// 以下都是右值
100; // 字面量(無名字,不能取地址)
a + b; // 表達式結果(臨時值,無名字)
func(); // 函數返回值(非引用類型時,是臨時值)

關鍵特征

  • 右值不能被取地址:&(a + b) 會編譯報錯(無法對臨時值取地址)。
  • 右值是 “消耗品”:使用后就會銷毀(除非被保存到左值中)。

右值的細分

C++11 后,右值又分為兩種,但日常使用中無需嚴格區(qū)分,知道它們都是右值即可:

  • 純右值(Prvalue):字面量(如 10)、表達式結果(如 a + b)、非引用返回的函數結果。
  • 將亡值(Xvalue):通過 std::move 轉換后的左值(本質是 “即將被銷毀的左值”,可被移動)。

2、左值引用:綁定左值的 “別名”

左值引用是最常用的引用類型,用 & 表示,只能綁定左值,本質是給左值起一個 “別名”,操作引用等價于操作原對象。

2.1 基本用法

int a = 10;
int& ref_a = a; // 正確:左值引用綁定左值(ref_a 是 a 的別名)
ref_a = 20; // 等價于 a = 20,a 現(xiàn)在是 20

2.2 左值引用的限制

不能綁定右值

// int& ref = 10; // 錯誤:左值引用不能綁定右值(10 是右值)

const 左值引用是特例:可以綁定右值(臨時延長右值的生命周期):

const int& ref = 10; // 正確:const 左值引用可綁定右值
// 原理:編譯器會生成臨時變量存儲 10,ref 綁定這個臨時變量(臨時變量生命周期被延長至 ref 相同)

2.3 左值引用的用途

避免函數傳參時的拷貝(如傳遞大對象 vector):

void func(const vector<int>& v) { ... } // 傳引用,無拷貝

允許函數修改外部變量(非 const 引用):

void increment(int& x) { x++; }
int a = 5;
increment(a); // a 變?yōu)?6

3、右值引用:綁定右值的 “專屬引用”

右值引用是 C++11 新增的引用類型,用 && 表示,專門綁定右值,目的是 “利用右值的臨時特性” 實現(xiàn)移動語義(避免不必要的拷貝)。

3.1 基本用法

int&& ref1 = 10; // 正確:右值引用綁定純右值(10 是右值)
int a = 10, b = 20;
int&& ref2 = a + b; // 正確:右值引用綁定表達式結果(右值)

3.2 右值引用的限制

不能直接綁定左值:

int a = 10;
// int&& ref = a; // 錯誤:右值引用不能直接綁定左值

但可以通過 std::move 將左值 “強制轉換” 為右值引用(本質是告訴編譯器:“這個左值可以被當作右值處理,資源可以被轉移”)

int a = 10;
int&& ref = std::move(a); // 正確:std::move 將 a 轉為右值引用

注意std::move 不會移動任何數據,只是 “標記” 左值為 “可被移動” 的右值,本身是編譯期操作,無運行時開銷。

3.3 右值引用的核心用途:移動語義

右值引用的最大價值是實現(xiàn)移動語義—— 對于臨時對象(右值),不再進行昂貴的拷貝,而是直接 “竊取” 其資源(如內存),大幅提升性能。

移動構造函數

class MyString {
private:
    char* data; // 存儲字符串的動態(tài)內存
public:
    // 普通構造函數
    MyString(const char* str) {
        size_t len = strlen(str);
        data = new char[len + 1];
        strcpy(data, str);
        cout << "構造函數:分配內存" << endl;
    }
    // 拷貝構造函數(左值引用參數,深拷貝)
    MyString(const MyString& other) {
        size_t len = strlen(other.data);
        data = new char[len + 1];
        strcpy(data, other.data);
        cout << "拷貝構造:深拷貝(性能差)" << endl;
    }
    // 移動構造函數(右值引用參數,直接竊取資源)
    MyString(MyString&& other) noexcept {
        data = other.data; // 直接接管 other 的內存
        other.data = nullptr; // other 放棄資源(避免析構時重復釋放)
        cout << "移動構造:竊取資源(性能好)" << endl;
    }
    ~MyString() {
        if (data) delete[] data;
        cout << "析構函數:釋放內存" << endl;
    }
};
int main() {
    MyString s1("hello"); // 調用普通構造
    MyString s2 = s1; // 調用拷貝構造(s1 是左值,必須深拷貝)
    MyString s3 = MyString("world"); // 調用移動構造(臨時對象是右值,直接竊取資源)
    MyString s4 = std::move(s1); // 調用移動構造(s1 被轉為右值,資源被 s4 竊?。?
    return 0;
}

輸出

構造函數:分配內存
拷貝構造:深拷貝(性能差)
構造函數:分配內存
移動構造:竊取資源(性能好)
移動構造:竊取資源(性能好)
析構函數:釋放內存
析構函數:釋放內存
析構函數:釋放內存

關鍵:移動構造函數通過右值引用參數,識別出臨時對象(或被 std::move 標記的左值),直接接管其資源,避免了拷貝開銷(對于大對象,性能提升顯著)。

4、萬能引用與完美轉發(fā)

在模板中,還有一種特殊的 “萬能引用”,它能同時接受左值和右值,并通過 “完美轉發(fā)” 保持原表達式的屬性(左值還是右值)。

4.1 萬能引用:能接受左值和右值的引用

萬能引用僅出現(xiàn)在模板參數中,形式為 T&&,其類型會根據傳入的參數自動推導:

  • 若傳入左值,T&& 會被推導為 “左值引用”(T&);
  • 若傳入右值,T&& 會被推導為 “右值引用”(T&&)。
template <typename T>
void func(T&& t) { // 萬能引用(僅模板中 T&& 才是萬能引用)
    // 根據傳入的參數,t 可能是左值引用或右值引用
}
int main() {
    int a = 10;
    func(a); // 傳入左值,T 推導為 int&,func 實際為 void func(int& t)
    func(20); // 傳入右值,T 推導為 int,func 實際為 void func(int&& t)
    return 0;
}

注意:萬能引用≠右值引用。只有模板中 T&& 且 T 是模板參數時,才是萬能引用;其他場景的 && 都是右值引用(如 void func(int&& t) 是右值引用)。

4.2 完美轉發(fā):保持原參數的左值 / 右值屬性

完美轉發(fā)是指在函數模板中,將參數通過萬能引用接收后,原封不動地轉發(fā)給其他函數(保持其左值 / 右值屬性)。需要配合 std::forward 實現(xiàn)。

為什么需要完美轉發(fā)?如果直接傳遞萬能引用參數,會丟失原屬性(因為引用本身是左值):

void target(int& x) { cout << "左值版本" << endl; }
void target(int&& x) { cout << "右值版本" << endl; }
template <typename T>
void func(T&& t) {
    target(t); // 錯誤:t 是引用(有名字),被當作左值處理,始終調用 target(int&)
}
int main() {
    int a = 10;
    func(a); // 傳入左值,期望調用 target(int&) → 實際正確
    func(20); // 傳入右值,期望調用 target(int&&) → 實際錯誤(調用了左值版本)
    return 0;
}

用 std::forward 實現(xiàn)完美轉發(fā):

template <typename T>
void func(T&& t) {
    target(std::forward<T>(t)); // 完美轉發(fā):保持 t 的原始屬性
}
int main() {
    int a = 10;
    func(a); // 傳入左值 → 轉發(fā)為左值 → 調用 target(int&)
    func(20); // 傳入右值 → 轉發(fā)為右值 → 調用 target(int&&)
    return 0;
}

原理std::forward<T>(t) 會根據 T 的類型(左值引用或右值引用),將 t 還原為原始的左值或右值屬性。

5、常見問題

  • 左值和右值有什么區(qū)別?
    • 左值:有標識符,可以取地址,生命周期較長,可以出現(xiàn)在賦值左邊
    • 右值:匿名臨時對象,不能取地址,生命周期短,只能出現(xiàn)在賦值右邊
  • 左值引用和右值引用有什么區(qū)別?
    • 左值引用 (T&):只能綁定到左值,用于創(chuàng)建別名
    • 右值引用 (T&&):只能綁定到右值,用于實現(xiàn)移動語義和完美轉發(fā)
  • std::move 做了什么?它真的移動數據嗎?
  • std::move 只是進行類型轉換,將左值轉換為右值引用
  • 它本身不移動任何數據,只是標記對象為"可移動的"
  • 實際的移動操作在移動構造函數移動賦值運算符中完成
  • 使用 std::move 后,原始對象會怎樣?
  • 對象處于"有效但未指定狀態(tài)"
  • 不應該再使用該對象,除非重新賦值
  • 析構函數仍然需要正常工作
  • 移動語義有什么優(yōu)勢?
    • 性能提升:避免不必要的深拷貝,特別是對于管理資源的類
    • 資源轉移:允許高效地轉移資源所有權
    • 支持不可拷貝對象:可以移動但不能拷貝的對象
  • std::forward 和 std::move 有什么區(qū)別?
    • std::move:無條件將左值轉為右值引用
    • std::forward:有條件地轉換,保持參數的原始值類別
  • 如何實現(xiàn)一個支持移動語義的字符串類?
class MyString {
private:
    char* data;
    size_t size;
public:
    // 移動構造函數
    MyString(MyString&& other) noexcept 
        : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
    }
    // 移動賦值運算符
    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
        }
        return *this;
    }
    // 析構函數
    ~MyString() {
        delete[] data;
    }
    // 禁用拷貝(可選)
    MyString(const MyString&) = delete;
    MyString& operator=(const MyString&) = delete;
};

什么情況下應該使用完美轉發(fā)?

應該在包裝函數、工廠函數、構造函數轉發(fā)等場景中使用完美轉發(fā),以保持參數的原始值類別。

// 包裝器函數
template<typename Func, typename... Args>
auto wrapper(Func&& func, Args&&... args) {
    return std::forward<Func>(func)(std::forward<Args>(args)...);
}
// 工廠函數
template<typename T, typename... Args>
T create(Args&&... args) {
    return T(std::forward<Args>(args)...);
}

右值引用本身是左值還是右值?

右值引用本身是左值。因為右值引用有名字、可以取地址(符合左值的特征)。

int&& ref = 10; // ref 是右值引用,但自身是左值
int& ref2 = ref; // 正確:ref 是左值,可綁定到左值引用

這也是為什么在轉發(fā)右值引用時,需要用 std::forward 才能還原其右值屬性。

const 左值引用為什么能綁定右值?

為了靈活性。const 左值引用設計的初衷之一是 “安全地引用臨時對象”,編譯器會為右值創(chuàng)建一個臨時變量,const 左值引用綁定這個臨時變量,并延長其生命周期(與引用同生命周期)。

用途:允許函數接受右值作為參數(如 void print(const string& s) 可接收字符串字面量 print("hello")),同時保證不修改原對象(const 約束)。

什么時候該用移動語義?

函數返回局部對象時

傳遞臨時對象給函數時

容器重新分配內存時

資源管理類(如智能指針)傳遞所有權時

什么是返回值優(yōu)化(RVO)?與移動語義的關系?

返回值優(yōu)化(RVO)是編譯器優(yōu)化技術,允許直接在調用者內存中構造返回對象,避免拷貝。與移動語義的關系:

RVO優(yōu)先級高于移動語義

當RVO不可用時,編譯器使用移動語義

C++17強制要求部分場景的RVO(稱為"guaranteed copy elision")

6、總結

左值/右值
├── 左值 (lvalue):有名字、可尋址
├── 右值 (rvalue):臨時對象、字面量
│   ├── 純右值 (prvalue)
│   └── 將亡值 (xvalue)
│
引用類型
├── 左值引用 (&):綁定左值
├── 右值引用 (&&):綁定右值
└── 通用引用 (T&&):模板推導
    └── 完美轉發(fā) (std::forward)

到此這篇關于C++ 左值、右值、左值引用、右值引用的文章就介紹到這了,更多相關C++ 左值、右值、左值引用、右值引用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C語言運算符及其優(yōu)先級匯總表口訣

    C語言運算符及其優(yōu)先級匯總表口訣

    由于C語言的運算符優(yōu)先級與C++的不完全一樣(主要是增加了幾個運算符),所以這個口訣不能完全實用于C++.但是應該能夠兼容,大家可以比較一下他們的區(qū)別應該就能夠很快掌握C++的優(yōu)先級的
    2013-07-07
  • C++三元表達式詳情

    C++三元表達式詳情

    這篇文章主要介紹了C++三元表達式,文章圍繞C++三元表達式的相關資料展開詳細內容,需要的朋友可以參考一下,希望多你有所幫助
    2021-11-11
  • C++中const用法小結

    C++中const用法小結

    C++ const 允許指定一個語義約束,編譯器會強制實施這個約束,允許程序員告訴編譯器某值是保持不變的。如果在編程中確實有某個值保持不變,就應該明確使用const,這樣可以獲得編譯器的幫助。
    2016-04-04
  • C語言中的字符串數據在C中的存儲方式

    C語言中的字符串數據在C中的存儲方式

    這篇文章主要介紹了C語言中的字符串數據在C中的存儲方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • C語言實現(xiàn)面向對象的方法詳解

    C語言實現(xiàn)面向對象的方法詳解

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)面向對象的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-08-08
  • OpenCV使用鼠標響應裁剪圖像

    OpenCV使用鼠標響應裁剪圖像

    這篇文章主要為大家詳細介紹了OpenCV實現(xiàn)鼠標響應裁剪圖像,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • 一元多項式加法運算

    一元多項式加法運算

    今天小編就為大家分享一篇關于一元多項式加法運算,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • 嵌入式C語言輕量級程序架構內核編寫

    嵌入式C語言輕量級程序架構內核編寫

    這篇文章主要介紹了嵌入式C語言輕量級程序架構內核編寫,文章將讓大家學到輕量級程序架構的內核實現(xiàn)原理、輕量級程序架構的設計思想、了解單片機常用的程序架構等更多C語言輕量級程序架構相關內容,需要的朋友可以參考一下
    2022-03-03
  • C++中的類模板詳解及示例

    C++中的類模板詳解及示例

    我們在定義函數時,可以通過定義函數模板,來簡化一些功能相同而數據類型不同的函數的定義和調用過程
    2013-10-10
  • operator new在C++中的各種寫法總結

    operator new在C++中的各種寫法總結

    這篇文章并不是一個綜合的手冊,而是一個C++中各種內存分配方法的概述。它面向已經很熟悉C++語言的讀者
    2013-09-09

最新評論