Kopierkonstruktoren
Ein Kopierkonstruktor ist ein Konstruktor, der mit einem Argument vom selben Klassentyp aufgerufen werden kann und den Inhalt des Arguments kopiert, ohne das Argument zu verändern.
Inhalt |
[bearbeiten] Syntax
klassenname (parameterliste ); |
(1) | ||||||||
klassenname (parameterliste ) funktionsrumpf |
(2) | ||||||||
klassenname (einzelparameterliste ) = default; |
(3) | (seit C++11) | |||||||
klassenname (parameterliste ) = delete; |
(4) | (seit C++11) | |||||||
klassenname ::klassenname (parameterliste ) funktionsrumpf |
(5) | ||||||||
klassenname ::klassenname (einzelparameterliste ) = default; |
(6) | (seit C++11) | |||||||
| Klassenname | - | die Klasse, deren Kopierkonstruktor deklariert wird |
| parameter-liste | - | eine nicht-leere Parameterliste, die alle folgenden Bedingungen erfüllt
|
| einzelparameterliste | - | eine Parameterliste mit nur einem Parameter, der vom Typ T&, const T&, volatile T& oder const volatile T& ist und kein Standardargument hat |
| function-body | - | der Funktionsrumpf des Kopierkonstruktors |
[bearbeiten] Erklärung
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 };
Der Kopierkonstruktor wird aufgerufen, wenn ein Objekt (durch direkte Initialisierung oder Kopierinitialisierung) von einem anderen Objekt desselben Typs initialisiert wird (es sei denn, die Überladungsauflösung wählt eine bessere Übereinstimmung oder der Aufruf wird eliminiert), was Folgendes einschließt:
- Initialisierung: T a = b; oder T a(b);, wobei b vom Typ
Tist; - Funktionsargumentübergabe: f(a);, wobei a vom Typ
Tist und f ist void f(T t); - Funktionsrückgabe: return a; innerhalb einer Funktion wie T f(), wobei a vom Typ
Tist und die keine Verschiebekonstruktor hat.
[bearbeiten] Implizit deklarierter Kopierkonstruktor
Wenn für einen Klassentyp keine benutzerdefinierten Kopierkonstruktoren bereitgestellt werden, deklariert der Compiler immer einen Kopierkonstruktor als nicht-explizites inline public Mitglied seiner Klasse. Dieser implizit deklarierte Kopierkonstruktor hat die Form T::T(const T&), wenn alle folgenden Bedingungen erfüllt sind
- Jeder direkte und virtuelle Basis
BvonThat einen Kopierkonstruktor, dessen Parameter vom Typ const B& oder const volatile B& sind; - Jedes nicht-statische Datenmitglied
MvonTvom Klassentyp oder Array vom Klassentyp hat einen Kopierkonstruktor, dessen Parameter vom Typ const M& oder const volatile M& sind.
Andernfalls ist der implizit deklarierte Kopierkonstruktor T::T(T&).
Aufgrund dieser Regeln kann der implizit deklarierte Kopierkonstruktor nicht an ein volatile Lvalue-Argument gebunden werden.
Eine Klasse kann mehrere Kopierkonstruktoren haben, z. B. sowohl T::T(const T&) als auch T::T(T&).
|
Selbst wenn einige benutzerdefinierte Kopierkonstruktoren vorhanden sind, kann der Benutzer die implizite Deklaration des Kopierkonstruktors immer noch mit dem Schlüsselwort default erzwingen. |
(seit C++11) |
Der implizit deklarierte (oder bei seiner ersten Deklaration standardmäßig gesetzte) Kopierkonstruktor hat eine Ausnahmespezifikation, wie in dynamische Ausnahmespezifikation(bis C++17)noexcept-Spezifikation(seit C++17) beschrieben.
[bearbeiten] Implizit definierter Kopierkonstruktor
Wenn der implizit deklarierte Kopierkonstruktor nicht gelöscht ist, wird er (d. h. ein Funktionsrumpf wird generiert und kompiliert), wenn er ODR-verwendet wird oder für die konstante Auswertung benötigt wird(seit C++11). Für Union-Typen kopiert der implizit definierte Kopierkonstruktor die Objektrepräsentation (wie mit std::memmove). Für Nicht-Union-Klassentypen führt der Konstruktor eine vollständige elementweise Kopie der direkten Basissubobjekte und Mitgliedssubobjekte des Objekts in ihrer Initialisierungsreihenfolge durch, unter Verwendung direkter Initialisierung. Für jedes nicht-statische Datenmitglied eines Referenztyps bindet der Kopierkonstruktor die Referenz an dasselbe Objekt oder dieselbe Funktion, an die die Quellreferenz gebunden ist.
|
Wenn dies die Anforderungen eines constexpr Konstruktors(bis C++23)constexpr Funktion(seit C++23) erfüllt, ist der generierte Kopierkonstruktor constexpr. Die Generierung des implizit definierten Kopierkonstruktors ist veraltet, wenn |
(seit C++11) |
[bearbeiten] Gelöschter Kopierkonstruktor
Der implizit deklarierte oder explizit standardmäßig gesetzte(seit C++11) Kopierkonstruktor für die Klasse T ist undefiniert(bis C++11)als gelöscht definiert(seit C++11), wenn eine der folgenden Bedingungen erfüllt ist
|
(seit C++11) |
-
That ein potenziell zu konstruierendes Subobjekt vom KlassentypM(oder möglicherweise ein mehrdimensionales Array davon), so dass
-
Meinen Destruktor hat, der gelöscht oder(seit C++11) vom Kopierkonstruktor aus nicht zugänglich ist, oder - die Überladungsauflösung bei der Suche nach dem Kopierkonstruktor von
M
- nicht zu einem benutzbaren Kandidaten führt, oder
- im Falle des Subobjekts als Varianten-Mitglied eine nicht-triviale Funktion auswählt.
-
|
Der implizit deklarierte Kopierkonstruktor für die Klasse |
(seit C++11) |
[bearbeiten] Trivialer Kopierkonstruktor
Der Kopierkonstruktor für die Klasse T ist trivial, wenn alle folgenden Bedingungen erfüllt sind
- er ist nicht benutzerdefiniert (d. h. er ist implizit definiert oder standardmäßig gesetzt);
-
Tkeine virtuellen Memberfunktionen hat; -
Tkeine virtuellen Basisklassen hat; - Der für jede direkte Basis von
Tausgewählte Kopierkonstruktor ist trivial; - Der für jedes nicht-statische Klassenmember (oder Array von Klassentyp)
Tausgewählte Kopierkonstruktor ist trivial;
Ein trivialer Kopierkonstruktor für eine Nicht-Union-Klasse kopiert effektiv jeden Skalar-Subobjekt (einschließlich rekursiv Subobjekte von Subobjekten und so weiter) des Arguments und führt keine weitere Aktion aus. Polsterbytes müssen jedoch nicht kopiert werden, und selbst die Objektrepräsentationen der kopierten Subobjekte müssen nicht identisch sein, solange ihre Werte gleich sind.
TriviallyCopyable Objekte können durch manuelles Kopieren ihrer Objektrepräsentationen kopiert werden, z. B. mit std::memmove. Alle mit der C-Sprache kompatiblen Datentypen (POD-Typen) sind trivial kopierbar.
[bearbeiten] Qualifizierter Kopierkonstruktor
|
Ein Kopierkonstruktor ist qualifiziert, wenn er entweder benutzerdefiniert deklariert oder implizit deklariert und definierbar ist. |
(bis C++11) |
|
Ein Kopierkonstruktor ist qualifiziert, wenn er nicht gelöscht ist. |
(seit C++11) (bis C++20) |
|
Ein Kopierkonstruktor ist qualifiziert, wenn alle folgenden Bedingungen erfüllt sind
|
(seit C++20) |
Die Trivialität von qualifizierten Kopierkonstruktoren bestimmt, ob die Klasse ein impliziter Lebenszeittyp ist und ob die Klasse ein trivial kopierbarer Typ ist.
[bearbeiten] Anmerkungen
In vielen Situationen werden Kopierkonstruktoren auch dann optimiert, wenn sie beobachtbare Nebeneffekte hätten, siehe Kopier-Elision.
[bearbeiten] Beispiel
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 }
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 1353 | C++98 | Die Bedingungen, unter denen implizit deklarierte Kopierkonstruktoren undefiniert waren, berücksichtigten keine mehrdimensionalen Array-Typen |
diese Typen berücksichtigen |
| CWG 2094 | C++11 | volatile Mitglieder machen Kopien nicht-trivial (CWG issue 496) | Trivialität nicht betroffen |
| CWG 2171 | C++11 | X(X&) = default war nicht-trivial | wurde trivial |
| CWG 2595 | C++20 | Ein Kopierkonstruktor war nicht qualifiziert, wenn es einen anderen Kopierkonstruktor gab, der stärker eingeschränkt war aber seine zugehörigen Constraints nicht erfüllte |
er kann in diesem Fall berechtigt sein |