複製建構函式
複製建構函式是一種建構函式,它可以使用相同類型別的引數呼叫,並複製引數的內容而不改變引數。
目錄 |
[編輯] 語法
類名 ( 引數列表 ); |
(1) | ||||||||
類名 ( 引數列表 ) 函式體 |
(2) | ||||||||
類名 ( 單一引數列表 ) = default; |
(3) | (C++11 起) | |||||||
類名 ( 引數列表 ) = delete; |
(4) | (C++11 起) | |||||||
類名 :: 類名 ( 引數列表 ) 函式體 |
(5) | ||||||||
類名 :: 類名 ( 單一引數列表 ) = default; |
(6) | (C++11 起) | |||||||
類名 | - | 宣告其複製建構函式的類 |
引數列表 | - | 非空的引數列表,滿足以下所有條件:
|
單一引數列表 | - | 僅含一個引數的引數列表,該引數型別為T&、const T&、volatile T& 或 const volatile T&,且沒有預設實參。 |
函式體 | - | 複製建構函式的函式體 |
[編輯] 解釋
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
,且f是void 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。 如果 |
(C++11 起) |
[編輯] 已刪除的複製建構函式
對於類T
,隱式宣告的或顯式預設的(C++11 起)複製建構函式在滿足以下任何條件時被未定義(C++11 前)定義為已刪除(C++11 起)
|
(C++11 起) |
-
T
有一個類型別M
(或其多維陣列)的潛在構造子物件,使得
-
M
有一個解構函式,該解構函式已刪除或(C++11 起)從複製建構函式中不可訪問,或 - 用於查詢
M
的複製建構函式的過載決議
- 未產生可用的候選,或者
- 在子物件是變體成員的情況下,選擇了一個非平凡函式。
-
(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 | 如果存在另一個複製建構函式,它更受約束 但沒有滿足其關聯約束 則複製建構函式不合格 |
它在這種情況下可以合格 |