名稱空間
變體
操作

複製建構函式

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

複製建構函式是一種建構函式,它可以使用相同類型別的引數呼叫,並複製引數的內容而不改變引數。

目錄

[編輯] 語法

類名 (引數列表 ); (1)
類名 (引數列表 ) 函式體 (2)
類名 (單一引數列表 ) = default; (3) (C++11 起)
類名 (引數列表 ) = delete; (4) (C++11 起)
類名 ::類名 (引數列表 ) 函式體 (5)
類名 ::類名 (單一引數列表 ) = default; (6) (C++11 起)
類名 - 宣告其複製建構函式的類
引數列表 - 非空的引數列表,滿足以下所有條件:
  • 給定類型別為T,第一個引數型別為T&const T&volatile T&const volatile T&,並且
  • 要麼沒有其他引數,要麼所有其他引數都帶有預設實參
單一引數列表 - 僅含一個引數的引數列表,該引數型別為T&const T&volatile T&const volatile T&,且沒有預設實參。
函式體 - 複製建構函式的函式體

[編輯] 解釋

1) 類定義內部的複製建構函式宣告。
2-4) 類定義內部的複製建構函式定義。
3) 複製建構函式是顯式預設的。
4) 複製建構函式是已刪除的。
5,6) 類定義外部的複製建構函式定義(類必須包含宣告(1))。
6) 複製建構函式是顯式預設的。
struct X
{
    X(X& other); // copy constructor
//  X(X other);  // Error: incorrect parameter type
};
 
union Y
{
    Y(Y& other, int num = 1); // copy constructor with multiple parameters
//  Y(Y& other, int num);     // Error: `num` has no default argument
};

當一個物件透過(直接初始化複製初始化)從另一個相同型別的物件進行初始化時,就會呼叫複製建構函式(除非過載決議選擇了更好的匹配或者呼叫被省略),這包括:

  • 初始化:T a = b;T a(b);,其中b的型別是T
  • 函式引數傳遞:f(a);,其中a的型別是T,且fvoid f(T t)
  • 函式返回:函式T f()內部的return a;,其中a的型別是T,並且沒有移動建構函式

[編輯] 隱式宣告的複製建構函式

如果一個類型別沒有提供使用者定義的複製建構函式,編譯器總是會宣告一個複製建構函式作為其類的非explicit inline public 成員。這個隱式宣告的複製建構函式形式為T::T(const T&),如果以下所有條件都為真:

  • T的每個直接和虛基類B都有一個複製建構函式,其引數型別為const B&const volatile B&
  • T的每個非靜態資料成員M,如果是類型別或類型別陣列,都有一個複製建構函式,其引數型別為const M&const volatile M&

否則,隱式宣告的複製建構函式是T::T(T&)

由於這些規則,隱式宣告的複製建構函式不能繫結到volatile左值引數。

一個類可以有多個複製建構函式,例如T::T(const T&)T::T(T&)

即使存在一些使用者定義的複製建構函式,使用者仍然可以使用關鍵字default強制宣告隱式複製建構函式。

(C++11 起)

隱式宣告的(或在其首次宣告時預設的)複製建構函式具有異常規範,如動態異常規範(C++17 前)noexcept 規範(C++17 起)中所述。

[編輯] 隱式定義的複製建構函式

如果隱式宣告的複製建構函式未被刪除,則當它被odr-use常量求值需要(C++11 起),編譯器會對其進行定義(即生成並編譯函式體)。對於聯合型別,隱式定義的複製建構函式會複製物件表示(如同透過std::memmove)。對於非聯合類型別,建構函式會按照初始化順序,使用直接初始化,對物件的直接基類子物件和成員子物件執行完整的成員逐位複製。對於引用型別的每個非靜態資料成員,複製建構函式將該引用繫結到與源引用繫結的相同物件或函式。

如果這滿足constexpr 建構函式(C++23 前)constexpr 函式(C++23 起)的要求,則生成的複製建構函式為constexpr

如果T有一個使用者定義的解構函式或使用者定義的複製賦值運算子,則隱式定義的複製建構函式的生成已被棄用。

(C++11 起)

[編輯] 已刪除的複製建構函式

對於類T,隱式宣告的或顯式預設的(C++11 起)複製建構函式在滿足以下任何條件時被未定義(C++11 前)定義為已刪除(C++11 起)

  • T有一個右值引用型別的非靜態資料成員。
(C++11 起)
  • M有一個解構函式,該解構函式已刪除或(C++11 起)從複製建構函式中不可訪問,或
  • 用於查詢M的複製建構函式的過載決議
  • 未產生可用的候選,或者
  • 在子物件是變體成員的情況下,選擇了一個非平凡函式。

如果類T聲明瞭移動建構函式移動賦值運算子,則該類的隱式宣告的複製建構函式被定義為已刪除。

(C++11 起)

[編輯] 平凡複製建構函式

如果以下所有條件都為真,則類T的複製建構函式是平凡的:

  • 它不是使用者提供的(即它是隱式定義的或預設的);
  • T 沒有虛成員函式;
  • T 沒有虛基類;
  • T的每個直接基類選擇的複製建構函式是平凡的;
  • T的每個非靜態類型別(或類型別陣列)成員選擇的複製建構函式是平凡的;

非聯合類的平凡複製建構函式實際上會複製引數的每個標量子物件(包括遞迴地,子物件的子物件等),並且不執行其他操作。然而,填充位元組不必複製,即使複製的子物件的物件表示不必相同,只要它們的值相同即可。

平凡可複製物件可以透過手動複製其物件表示來複制,例如使用std::memmove。所有與 C 語言相容的資料型別(POD 型別)都是平凡可複製的。

[編輯] 合格的複製建構函式

如果複製建構函式是使用者宣告的,或者既是隱式宣告的又是可定義的,那麼它就是合格的。

(C++11 前)

如果複製建構函式未被刪除,那麼它就是合格的。

(C++11 起)
(C++20 前)

如果滿足以下所有條件,則複製建構函式是合格的:

  • 它未被刪除。
  • 關聯約束(如果有)已滿足。
  • 沒有其他滿足其關聯約束的複製建構函式更受約束
(C++20 起)

合格複製建構函式的平凡性決定了該類是否是隱式生命週期型別,以及該類是否是平凡可複製型別

[編輯] 注意

在許多情況下,即使複製建構函式會產生可觀察的副作用,它們也會被最佳化掉,參見複製省略

[編輯] 示例

struct A
{
    int n;
    A(int n = 1) : n(n) {}
    A(const A& a) : n(a.n) {} // user-defined copy constructor
};
 
struct B : A
{
    // implicit default constructor B::B()
    // implicit copy constructor B::B(const B&)
};
 
struct C : B
{
    C() : B() {}
private:
    C(const C&); // non-copyable, C++98 style
};
 
int main()
{
    A a1(7);
    A a2(a1); // calls the copy constructor
 
    B b;
    B b2 = b;
    A a3 = b; // conversion to A& and copy constructor
 
    volatile A va(10);
    // A a4 = va; // compile error
 
    C c;
    // C c2 = c; // compile error
}

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 1353 C++98 隱式宣告的複製建構函式的條件
未定義時未考慮多維陣列型別
考慮這些型別
CWG 2094 C++11 volatile 成員使複製非平凡(CWG 問題 496 平凡性不受影響
CWG 2171 C++11 X(X&) = default 曾是非平凡的 現在是平凡的
CWG 2595 C++20 如果存在另一個複製建構函式,它更受約束
但沒有滿足其關聯約束
則複製建構函式不合格
它在這種情況下可以合格

[編輯] 參見