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

C++移動操作,RVO和NRVO詳細(xì)

 更新時間:2021年09月30日 08:53:57   作者:VisualGMQ  
本文將討論了何時C++會自動進(jìn)行移動操作,并且說明了復(fù)制消除,RVO和NRVO優(yōu)的化等香瓜吧資料,需要的小伙伴可以參考一下

移動操作主要參考了cppreference 的這個說明,
優(yōu)化部分的主要的參考來自于stack overflow 的這篇文章

一、移動操作

1、移動操作有關(guān)的函數(shù)

和移動操作相關(guān)的類函數(shù)有兩個

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

A(A&& rhs);


移動賦值運算符:

A& operator=(A&& rhs);


注意這兩個函數(shù)的參數(shù)類型都不是const,這也是C++默認(rèn)會生成的函數(shù)聲明。
移動構(gòu)造函數(shù)用于在構(gòu)造類型的時候使用:

A a1;

// 使用std::move強(qiáng)制進(jìn)行移動
A a2 = std::move(a1);
或
A a2(std::move(a1));

而移動賦值運算符就是在賦值的時候進(jìn)行移動:

A a1;
A a2;
a1 = std::move(a2); // 使用move進(jìn)行強(qiáng)制移動

2、何時自動聲明移動構(gòu)造函數(shù)和賦值移動構(gòu)造函數(shù)

隱式的移動構(gòu)造函數(shù)將會在可以被生成且滿足如下所有條件的情況下自動生成:

  • 沒有用戶聲明的 復(fù)制構(gòu)造函數(shù)
  • 沒有用戶聲明的 復(fù)制賦值運算符(即operator=(const A&)這類)
  • 沒有用戶聲明的 移動賦值運算符(即operator=(A&&)這類)
  • 沒有用戶聲明的 析構(gòu)函數(shù)

所謂可以被生成的意思是滿足以下所有條件:

  • 類中沒有不能移動的非靜態(tài)成員
  • 繼承時,基類可以被移動
  • 繼承時,基類的構(gòu)造函數(shù)可以被訪問

而移動賦值運算符的產(chǎn)生條件也差不多,只不過將沒有聲明的 移動賦值構(gòu)造函數(shù)改成沒有用戶聲明 移動構(gòu)造函數(shù)即可。

總之,這兩個函數(shù)生成的條件就一句話:除了普通的構(gòu)造函數(shù)外(指默認(rèn)構(gòu)造函數(shù)和帶其他參數(shù)的構(gòu)造函數(shù)),不得聲明任何其他的構(gòu)造函數(shù),operator=函數(shù)和析構(gòu)函數(shù)。

3、何時自動移動

使用std::move是一種強(qiáng)制的,顯式的移動。但是C++很多時候為了效率會自動幫我們移動。主要的規(guī)則其實就是所有的右值都會進(jìn)行移動,如果不能移動,進(jìn)行拷貝。但是為了嚴(yán)謹(jǐn),我們還是擺出cppreference上的規(guī)則:

  • 初始化的時候使用std::move():T a = std::move(b)或者T a(std::move(b));這種。這里要加上std::move(),不然會調(diào)用復(fù)制構(gòu)造函數(shù)。
  • 函數(shù)實參傳遞的時候使用std::move() :func(std::move(a))
  • 函數(shù)返回時,如:
class A {};

A CreateA() {
 return A();
}

// call

A a = CreateA();

的時候,使用A()產(chǎn)生的變量會首先移動到CreateA()函數(shù)產(chǎn)生的返回值中,這個時候這個返回值是一個臨時變量(我們記為temp),接下來就是執(zhí)行這段代碼:A a = temp,然后temp是臨時變量, 會再次調(diào)用A的移動構(gòu)造函數(shù)給a變量。

前兩個是屬于顯式的移動,最后一種就是隱式移動。移動賦值運算符的規(guī)則也是一樣,只有等號右邊是臨時變量就會自動調(diào)用。

二、復(fù)制消除、RVO和NRVO

雖然C++對移動操作定義的很明確,但編譯器卻并不總是按照這個定義去做。因為編譯器中有三個重要的優(yōu)化經(jīng)常會減少拷貝,甚至是移動操作。

GCCClang下可以添加-fno-elide-constructors選項來關(guān)閉這三種優(yōu)化。

1、復(fù)制消除

來看一看下面代碼:

class C {
public:
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
  C(C&& rhs) { std::cout << "A move was made.\n"; }

};

C f() {
  return C();
}

int main() {
  std::cout << "Hello World!\n";
  C obj = f();
}

這里建議在C++17標(biāo)準(zhǔn)下編譯,因為C++17起所有的復(fù)制消規(guī)則除被寫在語言規(guī)范內(nèi),大部分編譯器應(yīng)該都會做這件事。我的Clang++ 12.0.5上的執(zhí)行結(jié)果僅僅是輸出了一行Hello World:

Hello World!

按照上面的規(guī)則,函數(shù)在返回的時候會進(jìn)行移動,也就是說在f()的調(diào)用內(nèi),會先移動給臨時變量,然后臨時變量再移動給obj,但是這里什么都沒發(fā)生,沒有任何的移動和拷貝,obj就像憑空出現(xiàn)了一樣。

在C++17起,復(fù)制消除是強(qiáng)制執(zhí)行的,而C++11中是看編譯器心情。
在如下條件下會進(jìn)行復(fù)制消除:

  • return語句中,return的值是和函數(shù)返回值類型一樣的右值。類型一樣是為了防止隱式轉(zhuǎn)換,否則會產(chǎn)生新的變量從而阻止移動,右值是因為C++自動移動只能對右值操作。
  • 在變量初始化的時候,初始化表達(dá)式是右值。如:
class A{};

A f() { return A(); } // 這里是第一種情況,會自動復(fù)制消除

// call
A a = f(); // 這里函數(shù)返回值的臨時變量到a的過程中的移動也會被消除

這也就解釋了為什么上面的代碼沒有調(diào)用任何的拷貝,移動函數(shù)了。

2、RVO和NRVO

RVO是Return Value Optimization(返回值優(yōu)化)的簡寫,而NRVONamed Return Value Optimization(命名返回值優(yōu)化)的簡寫。這兩個優(yōu)化是復(fù)制消除的常見形式。
通過他們的名字就可以看出,這是在函數(shù)返回的時候做的優(yōu)化。

RVO是指在函數(shù)返回一個臨時變量時的優(yōu)化,具體的優(yōu)化如下:

// 原本的函數(shù)
T CreateT(int value) {
 return T(value);
}

T a = CreateT(10);

// 優(yōu)化后的函數(shù)(偽代碼):
void CreateT(T& v, int value) {
 v.T::T(value); // 直接在內(nèi)部進(jìn)行構(gòu)造
}

即通過將要接收函數(shù)返回值的對象以引用的形式放入函數(shù)內(nèi)部初始化,這樣就避免了一次移動/拷貝。

而NRVO則是更加寬泛的RVO。對于如下的代碼可以執(zhí)行NRVO:

T CreateT(int values) {
 T t(value);
 return t;
}

編譯器也會優(yōu)化成上面RVO優(yōu)化的樣子。

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

相關(guān)文章

  • C++詳細(xì)講解對象的構(gòu)造

    C++詳細(xì)講解對象的構(gòu)造

    當(dāng)在參數(shù)化構(gòu)造函數(shù)中聲明對象時,必須將初始值作為參數(shù)傳遞給構(gòu)造函數(shù)。對象聲明的常規(guī)方法可能不起作用。構(gòu)造函數(shù)可以顯式或隱式調(diào)用,讓我們一起了解對象的構(gòu)造
    2022-04-04
  • C++ 實戰(zhàn)開發(fā)一個猜單詞的小游戲

    C++ 實戰(zhàn)開發(fā)一個猜單詞的小游戲

    眾所周知紙上得來終覺淺,我們要在實戰(zhàn)中才能真正的掌握技術(shù),小編為大家?guī)硪环萦肅++編寫的猜單詞小游戲,給大家練練手,快來看看吧
    2021-11-11
  • 淺析_tmain()與main()的區(qū)別

    淺析_tmain()與main()的區(qū)別

    _tmain()是為了支持unicode所使用的main一個別名,既然是別名,應(yīng)該有宏定義過的,在哪里定義的呢?就在那個讓你困惑的<stdafx.h>里
    2013-03-03
  • Qt 添加MSVC2017編譯器的完整教程(保姆級)

    Qt 添加MSVC2017編譯器的完整教程(保姆級)

    本文主要介紹了Qt 添加MSVC2017編譯器的完整教程,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • Qt實現(xiàn)編寫SMTP客戶端的示例詳解

    Qt實現(xiàn)編寫SMTP客戶端的示例詳解

    這篇文章主要介紹了如何通過Qt實現(xiàn)編寫SMTP客戶端,可以實現(xiàn)通過SMTP發(fā)送電子郵件(有文本,html,附件,內(nèi)聯(lián)文件等的MIME)。并支持SSL和SMTP身份驗證,感興趣的可以學(xué)習(xí)一下
    2022-11-11
  • C語言植物大戰(zhàn)數(shù)據(jù)結(jié)構(gòu)快速排序圖文示例

    C語言植物大戰(zhàn)數(shù)據(jù)結(jié)構(gòu)快速排序圖文示例

    這篇文章主要為大家介紹了C語言植物大戰(zhàn)數(shù)據(jù)結(jié)構(gòu)快速排序圖文示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • c語言實現(xiàn)一個簡單日歷

    c語言實現(xiàn)一個簡單日歷

    本文給大家分享的是一則使用C語言來實現(xiàn)的一個簡單日歷的代碼,根據(jù)項目需求,實現(xiàn)了3個簡單的小功能,推薦給大家,有需要的小伙伴可以參考下。
    2015-03-03
  • 基于Qt實現(xiàn)簡易GIF播放器的示例代碼

    基于Qt實現(xiàn)簡易GIF播放器的示例代碼

    這篇文章主要介紹了如何利用Qt設(shè)計一個簡易GIF播放器,可以播放GIF動畫。其基本功能有載入文件、播放、暫停、停止、快進(jìn)和快退,感興趣的可以了解一下
    2022-06-06
  • c++ String去除頭尾空格的方法

    c++ String去除頭尾空格的方法

    這篇文章主要介紹了c++ String去除頭尾空格的方法,非常具有實用價值,需要的朋友可以參考下
    2014-10-10
  • C語言結(jié)構(gòu)體定義的方法匯總

    C語言結(jié)構(gòu)體定義的方法匯總

    結(jié)構(gòu)體是一種工具,用這個工具可以定義自己的數(shù)據(jù)類型。下面通過本文給大家分享了C語言結(jié)構(gòu)體定義的方法匯總,需要的朋友參考下吧
    2017-12-12

最新評論