亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

概述

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

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

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

工作原理

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

對左值和右值的一個最常見的誤解是:等號左邊的就是左值,等號右邊的就是右值。左值和右值都是針對表達(dá)式而言的,左值是指表達(dá)式結(jié)束后依然存在的持久對象,右值是指表達(dá)式結(jié)束時就不再存在的臨時對象。一個區(qū)分左值與右值的便捷方法是:看能不能對表達(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;

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

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

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

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

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

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

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

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

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

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

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

區(qū)分清楚了左值與右值,我們再來看看左值引用。左值引用根據(jù)其修飾符的不同,可以分為非常量左值引用和常量左值引用。

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

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

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

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í)際上調(diào)用了三次構(gòu)造函數(shù)。盡管有些編譯器可以采用RVO(Return Value Optimization)來進(jìn)行優(yōu)化,但優(yōu)化工作只在某些特定條件下才能進(jìn)行??梢钥吹?,上面很普通的一個函數(shù)調(diào)用,由于存在臨時對象的拷貝,導(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";

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

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

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

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

使用案例

有了右值引用的概念,我們可以用它來實(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版本后,對類的自動生成規(guī)則有什么影響呢?唯一的影響就是,如果提供了move版本的構(gòu)造函數(shù),則不會生成默認(rèn)的構(gòu)造函數(shù)。另外,編譯器永遠(yuǎn)不會自動生成move版本的構(gòu)造函數(shù)和賦值函數(shù),它們需要你手動顯式地添加。

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

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

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

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

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

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

應(yī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之后都開始支持移動構(gòu)造和移動賦值,從而避免了在添加元素或重新分配內(nèi)存時不必要的數(shù)據(jù)拷貝。

3、字符串字面值。

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

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

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

總結(jié)

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

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

相關(guān)文章

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

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

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

    c/c++小游戲源代碼

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

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

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

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

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

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

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)共享單車管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    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ì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2022-03-03
  • 淺析結(jié)束程序函數(shù)exit, _exit,atexit的區(qū)別

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

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

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

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

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

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

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

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

最新評論