最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++移動語義詳細(xì)介紹使用

 更新時間:2023年01月27日 09:41:40   作者:Shawn-Summer  
首先,移動語義和完美轉(zhuǎn)發(fā)這兩個概念是在C++的模板編程的基礎(chǔ)上,新增的特性,主要是配合模板來使用。本篇會從C++的值類型,到移動拷貝與移動賦值來理解移動語義與完美轉(zhuǎn)發(fā)

1.移動構(gòu)造函數(shù)

移動語義就是使用移動構(gòu)造函數(shù)來構(gòu)造對象。

我們知道在類中如果存在指針數(shù)據(jù)成員,那么我們就一定要寫拷貝構(gòu)造函數(shù),進行深拷貝

如下所示,就是拷貝構(gòu)造函數(shù)的用法:

#include<iostream>
using namespace std;
class A
{
    public:
        int * ptr;
        A():ptr(new int(0))
        {
            cout<<"constructor\n";
        };
        A(const A & h):ptr(new int(*h.ptr))//拷貝構(gòu)造函數(shù)
        {
            cout<<"copy constructor\n";
            //deep copy
        };
        ~A()
        {
            cout<<"destructor\n";
            delete ptr;
        };
};
A getA()
{
    return A();
}
int main()
{
    A a=getA();
}
//g++ .\test.cpp -std=c++11 -fno-elide-constructors

constructor
copy constructor
destructor
copy constructor
destructor
destructor

可以知道上面代碼中,實際上產(chǎn)生了3個對象,在getA()函數(shù)中,使用默認(rèn)構(gòu)造函數(shù)產(chǎn)生一個對象,然后將其作為返回值時,又會通過拷貝構(gòu)造函數(shù)產(chǎn)生一個對象,然后在main()函數(shù)中,又會通過拷貝構(gòu)造函數(shù)構(gòu)造出對象a,所以總共有3個對象產(chǎn)生,我們這里的拷貝構(gòu)造函數(shù)是進行的深拷貝,所以就會開辟3塊內(nèi)存.

在C++11中,我們可以使用移動構(gòu)造函數(shù),對上述代碼進行優(yōu)化

#include<iostream>
using namespace std;
class A
{
    public:
        int * ptr;
        A():ptr(new int(0))
        {
            cout<<"constructor\n";
        };
        A(const A & h):ptr(new int(*h.ptr))//拷貝構(gòu)造函數(shù)
        {
            cout<<"copy constructor\n";
            //deep copy
        };
        A(A && h):ptr(h.ptr)//移動構(gòu)造函數(shù)
        {
            h.ptr=nullptr;
            cout<<"move constructor\n";
        }
        ~A()
        {
            cout<<"destructor\n";
            delete ptr;
        };
};
A getA()
{
    return A();
}
int main()
{
    A a=getA();
}
//g++ .\test.cpp -std=c++11 -fno-elide-constructors

constructor
move constructor
destructor
move constructor
destructor
destructor

移動構(gòu)造函數(shù),它是進行的淺拷貝,由于被移動的值會立即進行析構(gòu),所以我們不關(guān)心它,只需進行淺拷貝,將其開辟的內(nèi)存空間轉(zhuǎn)讓給別人。上述代碼中,也會構(gòu)造出3個對象,但是它們只開辟一塊內(nèi)存空間,這就是移動構(gòu)造函數(shù)的優(yōu)勢。

總之,我們發(fā)現(xiàn)移動構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)的區(qū)別,其實就是深拷貝和淺拷貝的區(qū)別,移動構(gòu)造函數(shù)的開銷更小,當(dāng)然我們關(guān)心的是,移動構(gòu)造函數(shù)何時會被觸發(fā)?在上面代碼中就是一個例子,將getA()中的局部匿名對象移動給返回值,然后將返回值移動給 main()中的a。

這里我們給出結(jié)論:移動構(gòu)造函數(shù)只有在使用右值或右值引用來構(gòu)造對象時才會調(diào)用

那么什么是右值?

getA()中的A()就是右值,getA()的返回值也是右值,所以用它們構(gòu)造對象時,會調(diào)用移動構(gòu)造函數(shù)

那么什么是右值引用?

顧名思義就是右值的引用

2.右值引用

在C++11中,我們將值劃分為:左值、右值(分為將亡值和純右值)

左值:可以取地址,有名字的值

右值:不能取地址,沒有名字的值

純右值:運算表達式,如1+2,或者和對象無關(guān)的字面值,如true,或者非引用的函數(shù)返回值,或者lambda表達式

將亡值:僅和右值引用相關(guān)的值,它包括:右值引用的函數(shù)返回值T&&,或者std::move的返回值,或者被轉(zhuǎn)換為T&&類型的函數(shù)返回值

注意:不管是純右值還是將亡值,它們的存活時間都很短。不要被將亡值的名稱所迷惑了,其實所以右值的都會即將消亡。

實際上,對于純右值和將亡值的定義很難給出,而且我們也不需要區(qū)分它們兩,但是,我們至少可以確定一個值是左值還是右值。

C++98中所提及的引用,在C++11中我們稱之為左值引用,即這個引用只能綁定左值,在C++11中我們提供了一種新的能夠綁定右值的引用,即右值引用。

我們知道左值引用實際是一個變量的別名,右值引用它實際是一個匿名變量的別名

#include<iostream>
using namespace std;
class A
{
    public:
        int * ptr;
        A():ptr(new int(0))
        {
            cout<<"constructor\n";
        };
        A(const A & h):ptr(new int(*h.ptr))//拷貝構(gòu)造函數(shù)
        {
            cout<<"copy constructor\n";
            //deep copy
        };
        A(A && h):ptr(h.ptr)//移動構(gòu)造函數(shù)
        {
            h.ptr=nullptr;
            cout<<"move constructor\n";
        }
        ~A()
        {
            cout<<"destructor\n";
            delete ptr;
        };
};
A getA()
{
    return A();
}
int main()
{
    A&& a=getA();//右值引用
}

constructor
move constructor
destructor
destructor

在上述代碼中getA()的返回值是一個右值,它是一個臨時值,如果我們寫成A a=getA();,那么這個臨時值給a進行移動構(gòu)造后就會立即被析構(gòu),而如果我們使用A&& a=getA();,那就意味著我們給這個臨時值進行續(xù)命,a就是這個臨時值的別名,所以上述代碼就會少一個對象的構(gòu)造。

總之,右值引用就是一種綁定右值的引用,實際上在C++98中,我們所知的const T &,這樣的引用,也可以綁定右值,他也叫做萬能引用,當(dāng)他綁定右值的時候它的作用和右值引用是一樣的,只不過這里的const是底層的,所以我們不能用其修改右值,所以右值引用綁定右值時,可以修改該右值,而當(dāng)萬能引用綁定右值時,我們不可以修改該右值

T& a;//左值引用,只能綁定非常量左值
T&& a;//右值引用,只能綁定非常量右值
const T& a;//萬能引用,它可以綁定一切值,但是它不能修改該值
const T&& a;//和萬能引用功能一樣(一般不使用)

我們仔細(xì)來思索一下右值引用的用處,從本質(zhì)上講,它是給右值進行續(xù)命,而從實踐上講,它就是用來移動語義的,但是移動語義的時候,我們希望修改原來的右值(看上面代碼中的移動構(gòu)造函數(shù),它實際上修改了右值),所以我們說const T&&這種是無用的,

我們在學(xué)習(xí)了C++11中的移動語義和右值引用知識后,我們要深知一個編程規(guī)矩:

只要類中有指針數(shù)據(jù)成員,就一定要重寫拷貝構(gòu)造函數(shù)和移動構(gòu)造函數(shù)

3.std::move()將左值強制轉(zhuǎn)換為右值引用

看一下下面這段代碼

#include<iostream>
using namespace std;
class A
{
    public:
        int * ptr;
        A():ptr(new int(0))
        {
            cout<<"constructor\n";
        };
        A(const A & h):ptr(new int(*h.ptr))//拷貝構(gòu)造函數(shù)
        {
            cout<<"copy constructor\n";
            //deep copy
        };
        A(A && h):ptr(h.ptr)//移動構(gòu)造函數(shù)
        {
            h.ptr=nullptr;
            cout<<"move constructor\n";
        }
        ~A()
        {
            cout<<"destructor\n";
            delete ptr;
        };
};
A&& getA()
{
    return std::move(A());
}
int main()
{
    A&& a=getA();
}

constructor
destructor

getA()中的A()是右值,為什么還要用std::move將其轉(zhuǎn)換為右值引用?因為A()是一個純右值,右值引用當(dāng)然可以綁定純右值,但是A()是一個局部對象,在函數(shù)中返回引用時,我們禁止返回局部對象的引用,但是當(dāng)我們使用std::move后,A()就會轉(zhuǎn)換為右值引用類型,這樣子就可以將其作為引用返回。這是一種返回局部對象引用的特殊方法。

注意,這是一個涉及原則的問題,匿名對象是純右值

class  A
{};
int main()
{
    A& a=A();//報錯,左值引用無法綁定純右值
}

實際上,std::move()等價于static_cast<T&&>(lvalue),即將左值轉(zhuǎn)換為右值引用。

但是,std::move()有一個bug,即被轉(zhuǎn)化為右值引用的左值,不會被立即析構(gòu)。

#include<iostream>
using namespace std;
class A
{
    public:
        int* ptr;
        A():ptr(new int(999)){}
        ~A(){delete ptr;}
        A(const A& h):ptr(new int(*h.ptr)){}
        A(A&& h):ptr(h.ptr)
        {
            h.ptr=nullptr;
        }
};
int main()
{
    A a;
    A b(std::move(a));
    cout<<*a.ptr<<endl;//報錯
}

上述代碼就會報錯,因為a被轉(zhuǎn)化為右值引用后,b會調(diào)用移動構(gòu)造函數(shù)來構(gòu)造它自己,而在移動構(gòu)造函數(shù)中,它將a.ptr置空

#include<utility>
class A
{
    public:
        int *ptr;
        A():ptr(new int(0)){}
        ~A(){delete ptr;}
        A(const A& h):ptr(new int(*h.ptr)){}
        A(A&& h):ptr(h.ptr){h.ptr=nullptr;}
};
class B
{
    public:
        int *ptr;
        A elem;
        B():ptr(new int(0)){}
        ~B(){delete ptr;}
        B(const B&h):ptr(new int(*h.ptr)),elem(h.elem){}
        B(B&& h):ptr(h.ptr),elem(std::move(h.elem)){h.ptr=nullptr;}
};

注意看,B(const B&h):ptr(new int(*h.ptr)),elem(h.elem){}中對elem的初始化使用的是A的拷貝構(gòu)造函數(shù),

B(B&& h):ptr(h.ptr),elem(std::move(h.elem)){h.ptr=nullptr;}中對elem的初始化使用的是是A的移動構(gòu)造函數(shù). 注意一點,即使這里我們忘記寫std::move()也并無大礙,它會自行調(diào)用拷貝構(gòu)造函數(shù),當(dāng)然這也會導(dǎo)致一些開銷,所以在做類開發(fā)的時候,在寫類的移動構(gòu)造函數(shù)的時候,總是要記得將類成員move成右值引用。

4.拷貝語義和移動語義

如果一個類支持拷貝構(gòu)造函數(shù)和拷貝賦值函數(shù),那么我們就稱該類具有拷貝語義;同樣的如果一個類支持移動構(gòu)造函數(shù)和移動賦值函數(shù),那么我們就稱該類具有移動語義。

當(dāng)然有些類是同時支持移動語義和拷貝語義的。

在C++98中的類基本都是只具有拷貝語義的,而在C++11中的基本所有類都支持移動語義,特別的,有些類只支持移動語義,而不支持拷貝語義,這種類,我們稱之為資源型類,即資源只能被移動而不能被拷貝,例如智能指針類unique_ptr,文件流ifstream等都是資源型類,在C++11中,我們可以通過一些工具來判斷一個類是否支持移動語義。

我們看一下下面的代碼

template <class T>
void swap(T& a, T& b)
{
    T tmp(move(a));
    a=move(b);
    b=move(tmp);
}

上述代碼中,如果T支持移動語義,那么它就會調(diào)用移動構(gòu)造函數(shù)和移動賦值函數(shù),而如果T只支持拷貝語義,那么它也可以調(diào)用拷貝構(gòu)造函數(shù)和拷貝賦值函數(shù)

我們關(guān)于移動語義的另一個話題是:異常。因為如果移動語義沒有完成,卻拋出異常,那么可能會導(dǎo)致產(chǎn)生懸掛指針。所以在C++11中我們同樣有std::move_if_noexcept()函數(shù)來檢測,移動構(gòu)造函數(shù)是否用noexcept修飾。

再討論一個關(guān)于編譯器優(yōu)化的問題,如今c++編譯器已經(jīng)非常優(yōu)化了,RVO機制,即所謂返回值優(yōu)化機制,他能幫你完成類似移動語義的智能優(yōu)化,但是要記住,編譯器優(yōu)化不是完全奏效的,最好還是自己提高代碼效率。

到此這篇關(guān)于C++移動語義詳細(xì)介紹使用的文章就介紹到這了,更多相關(guān)C++移動語義內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言入門篇--四大常量(字面,const修飾,宏,枚舉)及標(biāo)識符

    C語言入門篇--四大常量(字面,const修飾,宏,枚舉)及標(biāo)識符

    本篇文章是c語言基礎(chǔ)篇,主要講述一下常量,常量即不可被直接修改的量(const修飾的常變量可間接修改,后續(xù)文章會繼續(xù)說明)請大家持續(xù)關(guān)注腳本之家
    2021-08-08
  • Qt透明無邊框窗口的實現(xiàn)示例

    Qt透明無邊框窗口的實現(xiàn)示例

    這篇文章主要介紹了Qt透明無邊框窗口的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • 基于C++實現(xiàn)五子棋小游戲

    基于C++實現(xiàn)五子棋小游戲

    這篇文章主要為大家詳細(xì)介紹了基于C++實現(xiàn)五子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 二叉樹中葉子節(jié)點的統(tǒng)計和樹高問題

    二叉樹中葉子節(jié)點的統(tǒng)計和樹高問題

    今天小編就為大家分享一篇關(guān)于二叉樹中葉子節(jié)點的統(tǒng)計和樹高問題,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • C++ COM編程之接口背后的虛函數(shù)表

    C++ COM編程之接口背后的虛函數(shù)表

    這篇文章主要介紹了C++ COM編程之接口背后的虛函數(shù)表,COM的背后,就是接口,而接口的背后,就是虛函數(shù)表,需要的朋友可以參考下
    2014-10-10
  • Qt5.9實現(xiàn)簡單的多線程實例(類QThread)

    Qt5.9實現(xiàn)簡單的多線程實例(類QThread)

    Qt開啟多線程,主要用到類QThread。用一個類繼承QThread,然后重新改寫虛函數(shù)run()。具有一定的參考價值,感興趣的可以了解一下
    2021-09-09
  • C++ Eigen庫實現(xiàn)最小二乘擬合的示例代碼

    C++ Eigen庫實現(xiàn)最小二乘擬合的示例代碼

    Eigen 是一個線性算術(shù)的 C++ 模板庫,功能強大、快速、優(yōu)雅以及支持多平臺,本文主要為大家介紹了C++利用Eigen庫實現(xiàn)最小二乘擬合的示例代碼,希望對大家有所幫助
    2023-07-07
  • C++進程鏈接工具之通信器詳解

    C++進程鏈接工具之通信器詳解

    本文主要介紹了C++通信器的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-11-11
  • Qt5.14.2使用虛擬鍵盤的關(guān)鍵代碼

    Qt5.14.2使用虛擬鍵盤的關(guān)鍵代碼

    對于Qwidget程序,使用qtvirtualkeyboard彈出鍵盤之后,鍵盤會浮于表面。使用VirtualkeyboardPushView模塊,自動根據(jù)情況把輸入視圖往上面推移,這篇文章主要介紹了Qt5.14.2使用虛擬鍵盤的關(guān)鍵代碼,需要的朋友可以參考下
    2022-09-09
  • 如何在C++中實現(xiàn)一個正確的時間循環(huán)器詳解

    如何在C++中實現(xiàn)一個正確的時間循環(huán)器詳解

    這篇文章主要給大家介紹了關(guān)于如何在C++中實現(xiàn)一個正確的時間循環(huán)器的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10

最新評論

五大连池市| 新化县| 福海县| 奉节县| 读书| 绥德县| 通州市| 汝城县| 扎鲁特旗| 唐海县| 临安市| 淄博市| 广宗县| 远安县| 同心县| 绍兴县| 邵阳县| 邹城市| 广西| 灯塔市| 喀喇沁旗| 招远市| 沁阳市| 陆川县| 南华县| 万宁市| 涡阳县| 和林格尔县| 民丰县| 汕头市| 弋阳县| 上犹县| 鞍山市| 隆化县| 儋州市| 彭水| 开封县| 中牟县| 措美县| 墨玉县| 丹凤县|