首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

C++ 避免函數回傳物件觸發拷貝建構:explicit 拷貝建構子強制移動語義與 NRVO/RVO 編譯器優化

     編輯:更多程式設計知識

C++ 函數以值回傳區域物件時,歷經 C++98 複製、C++11 移動、C++17 強制複製省略 (Guaranteed Copy Elision) 三個時代。本文實測三種情境:**預設拷貝建構子**、**`explicit` 拷貝建構子**、**`unique_ptr` 移動建構子**,揭示編譯器實際行為與開發者可控制的手段。

測試類別定義


class Thing {
public:
    int member_;
    Thing() : member_(0) {}
    Thing(Thing& other) : member_(other.member_) { cout << "copy\n"; }      // 拷貝建構子
    Thing(Thing&& other) : member_(other.member_) { cout << "move\n"; }     // 移動建構子
};

Thing getThingByReturnValue() {
    Thing thing;
    thing.member_ = 1;
    cout << "in method addr: " << &thing.member_ << endl;
    return thing;  // 回傳區域物件
}

情境一:預設拷貝建構子 (非 explicit)


Thing th = getThingByReturnValue();
cout << "out method addr: " << &th.member_ << endl;

輸出:

in method addr: 003CF798
copy
out method addr: 003CF88C

**位址不同** → 發生拷貝 (或移動),編譯器選擇拷貝建構子。若啟用優化 (Release/O2),多數編譯器執行 **NRVO (Named Return Value Optimization)** 直接在呼叫端堆疊框架建構物件,**完全省略拷貝/移動**,輸出位址相同且無 "copy"/"move" 列印。

情境二:`explicit` 拷貝建構子


explicit Thing(Thing& other) : member_(other.member_) { cout << "copy\n"; }

輸出 (優化關閉時)**:

in method addr: 003EFC18
move
out method addr: 003EFD0C

**關鍵機制**:`explicit` 拷貝建構子**不可隱性轉換**,編譯器無法以隱性拷貝初始化回傳值,**被迫選擇移動建構子** (若存在)。位址仍不同,但語意轉為移動。

情境三:`unique_ptr` 回傳 (標準庫移動語義)


unique_ptr getBySmartPtr() {
    unique_ptr p(new int);
    return p;  // unique_ptr 刪除複製,只有移動
}
auto p = getBySmartPtr();

輸出**:

in method addr: -842150451
out method addr: -842150451

**位址相同** → **移動建構子實際僅轉移指標值**,未搬移記憶體。且編譯器對 `unique_ptr` 等標準庫型別極度優化,常直接 NRVO。

C++ 標準演進對回傳值優化的影響

實務指導原則

  1. 信任編譯器 NRVO/RVO:現代編譯器 (GCC/Clang/MSVC) 對簡單回傳區域變數幾乎 100% 優化,**無需手動干預**。
  2. 勿為「避免拷貝」而寫 `explicit` 拷貝建構子:破壞正常值語意,導致 STL 容器、演算法失效 (需 `CopyConstructible`)。
  3. 型別設計:
    • 資源管理類 → `unique_ptr`/`shared_ptr` 內部管理,提供移動、刪除複製。
    • 值型別 (POD-like) → 預設複製/移動,**勿宣告使用者定義拷貝建構子**,讓編譯器合成 trivial 操作以利優化。
  4. 回傳標準庫容器/智慧指標:直接 `return vec;` / `return up;`,C++17 保證零開銷。
  5. 效能熱點才手動優化:Profile 確認回傳值確為瓶頸時,考慮:
    • 傳出參數 `void getResult(Result& out)`
    • 回傳 `unique_ptr` (單一所有權)
    • 靜態/執行緒區域靜態物件回傳參考

程式碼片段:正確的資源管理類回傳模式


// 推薦:C++17 直接回傳 unique_ptr
unique_ptr createResource() {
    return make_unique(args...);  // C++14 make_unique
}

// C++11/14 相容寫法 (同樣受 NRVO/移動優化)
unique_ptr createResource() {
    unique_ptr p(new HeavyResource(args...));
    return p;  // 移動建構子 + NRVO
}

**總結**:C++17 起,回傳值優化為語言保證;C++11/14 依賴編譯器 NRVO + 移動語義。**專注於正確的所有權設計 (unique_ptr/值語意),而非微觀控制拷貝/移動。**

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
標準規則實際行為
C++98允許 NRVO (匿名/具名)編譯器自行決定,多數優化
C++11引入移動語義,NRVO 失效時退而求其次用移動需型別有移動建構子
C++17強制複製省略 (prvalue 直接初始化結果物件)**不再要求型別可複製/可移動**,零開銷保證