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

C++?11新特性之右值引用使用案例與應(yīng)用場(chǎng)景

 更新時(shí)間:2024年01月30日 11:28:56   作者:hope_wisdom  
右值引用和move語義是C++ 11中重要的特性之一,可以提高程序的效率和性能,右值引用是一種新的引用類型,下面這篇文章主要給大家介紹了關(guān)于C++?11新特性之右值引用使用案例與應(yīng)用場(chǎng)景的相關(guān)資料,需要的朋友可以參考下

概述

C++ 11中引入了一項(xiàng)關(guān)鍵特性——右值引用,極大地增強(qiáng)了C++在資源管理、性能優(yōu)化和表達(dá)力方面的能力。通過理解并合理運(yùn)用右值引用,我們可以編寫出更高效、更簡(jiǎn)潔且不易出錯(cuò)的代碼。本文將深入探討右值引用的概念、工作原理及其在C++編程實(shí)踐中的應(yīng)用場(chǎng)景。

右值引用是C++中的一種特殊引用類型,它只能綁定到臨時(shí)對(duì)象或即將銷毀的對(duì)象上,也就是那些沒有命名且不再需要的對(duì)象。語法上,右值引用以&&表示,可參考下面的示例代碼。

// 右值引用,綁定到字面量整數(shù)
int&& nData = 42;

工作原理

理解右值引用是學(xué)習(xí)“移動(dòng)語義”的基礎(chǔ),而要理解右值引用,就必須先區(qū)分左值與右值。

對(duì)左值和右值的一個(gè)最常見的誤解是:等號(hào)左邊的就是左值,等號(hào)右邊的就是右值。左值和右值都是針對(duì)表達(dá)式而言的,左值是指表達(dá)式結(jié)束后依然存在的持久對(duì)象,右值是指表達(dá)式結(jié)束時(shí)就不再存在的臨時(shí)對(duì)象。一個(gè)區(qū)分左值與右值的便捷方法是:看能不能對(duì)表達(dá)式取地址,如果能,則為左值,否則為右值。下面我們結(jié)合一些實(shí)際例子來進(jìn)行說明。

int a = 100;
int b = 200;
int *pFlag = &a;
vector<int> vctTemp;
vctTemp.push_back(66);
string str1 = "Hello ";
string str2 = "World";
const int &m = 99;

請(qǐng)問:a、b、a+b、a++、++a、pFlag、*pFlag、vctTemp[0]、100、string("CSDN")、str1、str1+str2、m分別是左值還是右值?

1、a和b都是持久對(duì)象(可以對(duì)其取地址),是左值。

2、a+b是臨時(shí)對(duì)象(不可以對(duì)其取地址),是右值。

3、a++是先取出持久對(duì)象a的一份拷貝,再使持久對(duì)象a的值加1,最后返回那份拷貝,而那份拷貝是臨時(shí)對(duì)象(不可以對(duì)其取地址),故其是右值。

4、++a則是使持久對(duì)象a的值加1,并返回那個(gè)持久對(duì)象a本身(可以對(duì)其取地址),故其是左值。

5、pFlag和*pFlag都是持久對(duì)象(可以對(duì)其取地址),是左值。

6、vctTemp[0]調(diào)用了重載的[]操作符,而[]操作符返回的是一個(gè)int &,為持久對(duì)象(可以對(duì)其取地址),是左值。

7、100和string("CSDN")是臨時(shí)對(duì)象(不可以對(duì)其取地址),是右值。

8、str1是持久對(duì)象(可以對(duì)其取地址),是左值。

9、str1+str2是調(diào)用了+操作符,而+操作符返回的是一個(gè)string(不可以對(duì)其取地址),故其為右值。

10、m是一個(gè)常量引用,引用到一個(gè)右值,但引用本身是一個(gè)持久對(duì)象(可以對(duì)其取地址),為左值。

區(qū)分清楚了左值與右值,我們?cè)賮砜纯醋笾狄谩W笾狄酶鶕?jù)其修飾符的不同,可以分為非常量左值引用和常量左值引用。

非常量左值引用只能綁定到非常量左值,不能綁定到常量左值、非常量右值和常量右值。如果允許綁定到常量左值和常量右值,則非常量左值引用可以用于修改常量左值和常量右值,這明顯違反了其常量的含義。如果允許綁定到非常量右值,則會(huì)導(dǎo)致非常危險(xiǎn)的情況出現(xiàn)。因?yàn)榉浅A坑抑凳且粋€(gè)臨時(shí)對(duì)象,非常量左值引用可能會(huì)使用一個(gè)已經(jīng)被銷毀了的臨時(shí)對(duì)象。

常量左值引用可以綁定到所有類型的值,包括:非常量左值、常量左值、非常量右值和常量右值。

可以看出,使用左值引用時(shí),我們無法區(qū)分出綁定的是否是非常量右值的情況。那么,為什么要對(duì)非常量右值進(jìn)行區(qū)分呢?區(qū)分出來了,又有什么好處呢?這就牽涉到C++中一個(gè)著名的性能問題——拷貝臨時(shí)對(duì)象??紤]下面的示例代碼。

vector<int> GetAllScores()
{
    vector<int> vctTemp;
    vctTemp.push_back(90);
    vctTemp.push_back(95);
    return vctTemp;
}

當(dāng)使用vector<int> vctScore = GetAllScores()進(jìn)行初始化時(shí),實(shí)際上調(diào)用了三次構(gòu)造函數(shù)。盡管有些編譯器可以采用RVO(Return Value Optimization)來進(jìn)行優(yōu)化,但優(yōu)化工作只在某些特定條件下才能進(jìn)行。可以看到,上面很普通的一個(gè)函數(shù)調(diào)用,由于存在臨時(shí)對(duì)象的拷貝,導(dǎo)致了額外的兩次拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)的開銷。當(dāng)然,我們也可以修改函數(shù)的形式為:void GetAllScores(vector<int> &vctScore),但這并不一定就是我們需要的形式。另外,考慮下面字符串的連接操作:

string s1("Hello ");
string s = s1 + "W" + "o" + "r" + "l" + "d";

在對(duì)s進(jìn)行初始化時(shí),會(huì)產(chǎn)生大量的臨時(shí)對(duì)象,并涉及到大量字符串的拷貝操作,這顯然會(huì)影響程序的效率和性能。怎么解決這個(gè)問題呢?如果我們能確定某個(gè)值是一個(gè)非常量右值(或者是一個(gè)以后不會(huì)再使用的左值),則我們?cè)谶M(jìn)行臨時(shí)對(duì)象的拷貝時(shí),可以不用拷貝實(shí)際的數(shù)據(jù),而只是“竊取”指向?qū)嶋H數(shù)據(jù)的指針(類似于STL中的auto_ptr,會(huì)轉(zhuǎn)移所有權(quán))。C++ 11中引入的右值引用,正好可用于標(biāo)識(shí)一個(gè)非常量右值。C++ 11中用&表示左值引用,用&&表示右值引用,比如:int &&a = 10。

右值引用根據(jù)其修飾符的不同,也可以分為:非常量右值引用和常量右值引用。

非常量右值引用只能綁定到非常量右值,不能綁定到非常量左值、常量左值和常量右值。如果允許綁定到非常量左值,則可能會(huì)錯(cuò)誤地竊取一個(gè)持久對(duì)象的數(shù)據(jù),而這是非常危險(xiǎn)的;如果允許綁定到常量左值和常量右值,則非常量右值引用可以用于修改常量左值和常量右值,這明顯違反了其常量的含義。

常量右值引用可以綁定到非常量右值和常量右值,不能綁定到非常量左值和常量左值(理由同上)。

使用案例

有了右值引用的概念,我們可以用它來實(shí)現(xiàn)下面的CMyString類。

#include <iostream>
using namespace std;

class CMyString
{
public:
    // 構(gòu)造函數(shù)
    CMyString(const char *pszSrc = NULL)
    {
        cout << "CMyString(const char *pszSrc = NULL)" << endl;
        if (pszSrc == NULL)
        {
            m_pData = new char[1];
            *m_pData = '\0';
        }
        else
        {
            m_pData = new char[strlen(pszSrc) + 1];
            strcpy(m_pData, pszSrc);
        }
    }

    // 拷貝構(gòu)造函數(shù)
    CMyString(const CMyString &s)
    {
        cout << "CMyString(const CMyString &s)" << endl;
        m_pData = new char[strlen(s.m_pData) + 1];
        strcpy(m_pData, s.m_pData);
    }

    // move構(gòu)造函數(shù)
    CMyString(CMyString &&s)
    {
        cout << "CMyString(CMyString &&s)" << endl;
        m_pData = s.m_pData;
        s.m_pData = NULL;
    }

    // 析構(gòu)函數(shù)
    ~CMyString()
    {
        cout << "~CMyString()" << endl;
        delete [] m_pData;
        m_pData = NULL;
    }

    // 拷貝賦值函數(shù)
    CMyString &operator =(const CMyString &s)
    {
        cout << "CMyString &operator =(const CMyString &s)" << endl;
        if (this != &s)
        {
            delete [] m_pData;
            m_pData = new char[strlen(s.m_pData) + 1];
            strcpy(m_pData, s.m_pData);
        }

        return *this;
    }

    // move賦值函數(shù)
    CMyString &operator =(CMyString &&s)
    {
        cout << "CMyString &operator =(CMyString &&s)" << endl;
        if (this != &s)
        {
            delete [] m_pData;
            m_pData = s.m_pData;
            s.m_pData = NULL;
        }

        return *this;
    }

private:
    char *m_pData;
};

int main()
{
    CMyString strText("Hello CSDN");
    CMyString strText2;
    strText2 = CMyString("BeiJing");
    return 0;
}

可以看到,上面我們添加了move版本的構(gòu)造函數(shù)和賦值函數(shù)。那么,添加了move版本后,對(duì)類的自動(dòng)生成規(guī)則有什么影響呢?唯一的影響就是,如果提供了move版本的構(gòu)造函數(shù),則不會(huì)生成默認(rèn)的構(gòu)造函數(shù)。另外,編譯器永遠(yuǎn)不會(huì)自動(dòng)生成move版本的構(gòu)造函數(shù)和賦值函數(shù),它們需要你手動(dòng)顯式地添加。

當(dāng)添加了move版本的構(gòu)造函數(shù)和賦值函數(shù)的重載形式后,某一個(gè)函數(shù)調(diào)用應(yīng)當(dāng)使用哪一個(gè)重載版本呢?下面是按照判決的優(yōu)先級(jí)列出的3條規(guī)則。

1、常量值只能綁定到常量引用上,不能綁定到非常量引用上。

2、左值優(yōu)先綁定到左值引用上,右值優(yōu)先綁定到右值引用上。

3、非常量值優(yōu)先綁定到非常量引用上。

當(dāng)給構(gòu)造函數(shù)或賦值函數(shù)傳入一個(gè)非常量右值時(shí),依據(jù)上面給出的判決規(guī)則,可以得出會(huì)調(diào)用move版本的構(gòu)造函數(shù)或賦值函數(shù)。而在move版本的構(gòu)造函數(shù)或賦值函數(shù)內(nèi)部,都是直接“移動(dòng)”了其內(nèi)部數(shù)據(jù)的指針。因?yàn)樗欠浅A坑抑担且粋€(gè)臨時(shí)對(duì)象,移動(dòng)了其內(nèi)部數(shù)據(jù)的指針不會(huì)導(dǎo)致任何問題,它馬上就要被銷毀了,我們只是重復(fù)利用了其內(nèi)存,這樣就省去了拷貝數(shù)據(jù)的大量開銷。

一個(gè)需要注意的地方是:拷貝構(gòu)造函數(shù)可以通過直接調(diào)用*this = s來實(shí)現(xiàn),但move構(gòu)造函數(shù)卻不能。這是因?yàn)樵趍ove構(gòu)造函數(shù)中,s雖然是一個(gè)非常量右值引用,但其本身卻是一個(gè)左值(是持久對(duì)象,可以對(duì)其取地址),因此調(diào)用*this = s時(shí),會(huì)使用拷貝賦值函數(shù)而不是move賦值函數(shù),而這已與move構(gòu)造函數(shù)的語義不相符。要使語義正確,我們需要將左值綁定到非常量右值引用上,C++ 11提供了move函數(shù)來實(shí)現(xiàn)這種轉(zhuǎn)換,因此我們可以修改為*this = move(s),這樣move構(gòu)造函數(shù)就會(huì)調(diào)用move賦值函數(shù)。

應(yīng)用場(chǎng)景

1、智能指針。

C++ 11標(biāo)準(zhǔn)庫中的std::unique_ptr和std::shared_ptr等智能指針利用了右值引用來安全有效地轉(zhuǎn)移所有權(quán)。

2、容器。

C++ STL容器,比如:std::vector、std::string等,在C++11之后都開始支持移動(dòng)構(gòu)造和移動(dòng)賦值,從而避免了在添加元素或重新分配內(nèi)存時(shí)不必要的數(shù)據(jù)拷貝。

3、字符串字面值。

C++ 11中引入了std::string_view,它可以通過右值引用高效地引用字符串字面值,或現(xiàn)有std::string對(duì)象的內(nèi)容,而無需復(fù)制。

4、RAII原則強(qiáng)化。

RAII,也就是資源獲取即初始化(英文為:Resource Acquisition Is Initialization),是C++中一種重要的資源管理手段。右值引用使資源在生命周期結(jié)束時(shí)能被自動(dòng)回收,并允許在對(duì)象之間高效地“移動(dòng)”資源控制權(quán)。

總結(jié)

右值引用無疑是C++ 11的一項(xiàng)重大革新,它不僅提高了程序的運(yùn)行效率,也極大地簡(jiǎn)化了程序員對(duì)資源管理的復(fù)雜度。掌握這一特性對(duì)于寫出更現(xiàn)代、更高效的C++代碼至關(guān)重要。同時(shí),隨著C++后續(xù)版本的發(fā)展,右值引用在標(biāo)準(zhǔn)庫中的使用越來越廣泛,成為現(xiàn)代C++開發(fā)者的必備知識(shí)之一。

到此這篇關(guān)于C++ 11新特性之右值引用使用案例與應(yīng)用場(chǎng)景的文章就介紹到這了,更多相關(guān)C++ 11新特性右值引用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言實(shí)現(xiàn)停車場(chǎng)管理

    C語言實(shí)現(xiàn)停車場(chǎng)管理

    這篇文章主要為大家詳細(xì)介紹了C語言課程設(shè)計(jì)之停車場(chǎng)管理問題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • c/c++小游戲源代碼

    c/c++小游戲源代碼

    這篇文章主要介紹了c/c++小游戲源代碼,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • C++實(shí)現(xiàn)圖的鄰接表存儲(chǔ)和廣度優(yōu)先遍歷實(shí)例分析

    C++實(shí)現(xiàn)圖的鄰接表存儲(chǔ)和廣度優(yōu)先遍歷實(shí)例分析

    這篇文章主要介紹了C++實(shí)現(xiàn)圖的鄰接表存儲(chǔ)和廣度優(yōu)先遍歷,實(shí)例分析了C++實(shí)現(xiàn)圖的存儲(chǔ)與遍歷技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • C++實(shí)現(xiàn)類似延時(shí)停頓的打字效果

    C++實(shí)現(xiàn)類似延時(shí)停頓的打字效果

    這篇文章主要介紹的是使用C++實(shí)現(xiàn)類似延時(shí)停頓的打字效果的代碼,非常的簡(jiǎn)單,推薦給大家,有需要的小伙伴可以參考下。
    2015-03-03
  • C語言實(shí)現(xiàn)共享單車管理系統(tǒng)

    C語言實(shí)現(xiàn)共享單車管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)共享單車管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 詳解C語言結(jié)構(gòu)體中的char數(shù)組如何賦值

    詳解C語言結(jié)構(gòu)體中的char數(shù)組如何賦值

    這篇文章主要給大家介紹了關(guān)于C語言結(jié)構(gòu)體中的char數(shù)組如何賦值的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-03-03
  • 淺析結(jié)束程序函數(shù)exit, _exit,atexit的區(qū)別

    淺析結(jié)束程序函數(shù)exit, _exit,atexit的區(qū)別

    在一個(gè)程序中最多可以用atexit()注冊(cè)32個(gè)處理函數(shù),這些處理函數(shù)的調(diào)用順序與其注冊(cè)的順序相反,也即最先注冊(cè)的最后調(diào)用,最后注冊(cè)的最先調(diào)用
    2013-09-09
  • 基于Qt實(shí)現(xiàn)日志打印系統(tǒng)

    基于Qt實(shí)現(xiàn)日志打印系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了如何利用Qt開發(fā)一個(gè)日志打印系統(tǒng),可以實(shí)現(xiàn)打印日志按日期、大小保存,過期刪除,窗口實(shí)時(shí)顯示日志,網(wǎng)絡(luò)傳輸日志遠(yuǎn)程調(diào)試,需要的可以參考下
    2023-12-12
  • C++深入講解初始化列表的用法

    C++深入講解初始化列表的用法

    這篇文章主要介紹了C++成員初始化列表,除了可以使用構(gòu)造函數(shù)對(duì)類成員進(jìn)行初始化之外,C++還提供了另外一種初始化的方法,叫做成員初始化列表。下面來看看文章的詳細(xì)吧,需要的朋友可以參考一下
    2022-04-04
  • C++實(shí)現(xiàn)哈夫曼編碼

    C++實(shí)現(xiàn)哈夫曼編碼

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)哈夫曼編碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04

最新評(píng)論