Was sind alle Member-Funktionen, die vom Compiler für eine Klasse erstellt werden? Passiert das ständig?

Lesezeit: 8 Minuten

Was sind alle Member Funktionen die vom Compiler fur eine Klasse
Onnesh

Was sind alle Member-Funktionen, die vom Compiler für eine Klasse erstellt werden? Passiert das ständig? wie Destruktor. Meine Sorge ist, ob es für alle Klassen erstellt wird und warum der Standardkonstruktor benötigt wird?

C++98/03

Wenn sie benötigt werden,

  1. Der Compiler generiert eine Standardkonstruktor für Sie, es sei denn, Sie deklarieren einen eigenen Konstruktor.
  2. Der Compiler generiert eine Kopieren Konstrukteur für Sie, es sei denn, Sie deklarieren Ihre eigenen.
  3. Der Compiler generiert eine Kopieren Aufgabenverwalter für Sie, es sei denn, Sie deklarieren Ihre eigenen.
  4. Der Compiler generiert eine Zerstörer für Sie, es sei denn, Sie deklarieren Ihre eigenen.

Wie Péter in einem hilfreichen Kommentar sagte, werden all diese nur vom Compiler generiert wenn sie gebraucht werden. (Der Unterschied besteht darin, dass es in Ordnung ist, wenn der Compiler sie nicht erstellen kann, solange sie nicht verwendet werden.)


C++11

C++11 fügt die folgenden Regeln hinzu, die auch für C++14 gelten (Credits towi, siehe diesen Kommentar):

  • Der Compiler generiert die Bewegung Konstrukteur wenn
    • es ist kein Benutzer deklariert Kopieren Konstrukteurund
    • es ist kein Benutzer deklariert Kopieren Aufgabenverwalterund
    • es ist kein Benutzer deklariert Bewegung Aufgabenverwalter und
    • es ist kein Benutzer deklariert Zerstörer,
    • es ist nicht markiert deleteD,
    • und alle Mitglieder und Basen sind beweglich.
  • Ähnlich für Bewegung Aufgabenverwalteres wird generiert, wenn
    • es ist kein Benutzer deklariert Kopieren Konstrukteurund
    • es ist kein Benutzer deklariert Kopieren Aufgabenverwalterund
    • es ist kein Benutzer deklariert Bewegung Konstrukteur und
    • es ist kein Benutzer deklariert Zerstörer,
    • es ist nicht markiert deleteD,
    • und alle Mitglieder und Basen sind beweglich.

Beachten Sie, dass diese Regeln etwas ausgefeilter sind als die C++03-Regeln und in der Praxis sinnvoller sind.

Zum leichteren Verständnis dessen, was oben steht:

class Thing {
public:
    Thing();                        // default constructor
    Thing(const Thing&);            // copy c'tor
    Thing& operator=(const Thing&); // copy-assign
    ~Thing();                       // d'tor
    // C++11:
    Thing(Thing&&);                 // move c'tor
    Thing& operator=(Thing&&);      // move-assign
};

Weiterführende Literatur: Wenn Sie ein C++-Anfänger sind, ziehen Sie ein Design in Betracht, bei dem Sie keines der fünf aka implementieren müssen Die Nullregel ursprünglich aus ein Artikel geschrieben von Martinho Fernandes.

  • Aus Gründen der Genauigkeit werden all diese nur dann generiert, wenn sie tatsächlich benötigt werden, nicht immer. ZB wird der Zuweisungsoperator nur generiert, wenn tatsächlich eine Zuweisung an eine Instanz der betreffenden Klasse stattfindet.

    – Peter Török

    17. September 2010 um 9:57 Uhr


  • @sbi: Das tut es. Die Regeln sind etwas komplexer – von dem, was ich verstehe, um sicherzustellen, dass sich Klassen im C++ 03-Stil wie erwartet verhalten. Ich bin kein Experte für 0x, aber ich verstehe, dass ein Bewegungskonstruktor nicht generiert wird, wenn die Klasse einen vom Benutzer deklarierten Kopierkonstruktor hat. Sie können eine deklarieren = default wenn Sie die Standardimplementierung wieder zurückhaben möchten.

    – CB Bailey

    17. September 2010 um 9:59 Uhr

  • @Charles: Obwohl die standardmäßigen Verschiebevorgänge wahrscheinlich eine unbeabsichtigte Semantik haben, wenn die standardmäßigen Kopiervorgänge das Falsche tun und daher manuell bereitgestellt werden mussten.

    – fredoverflow

    17. September 2010 um 10:04 Uhr

  • @FredOverflow: Einverstanden. Es wäre eine bizarre Situation, wenn ein benutzerdefinierter Kopierkonstruktor erforderlich wäre, der Standard-Verschiebekonstruktor jedoch einwandfrei funktionierte.

    – CB Bailey

    17. September 2010 um 10:16 Uhr

  • @sbi: Das tut es. Der Compiler generiert die Bewege C’tor wenn kein Benutzer definiert ist Kopieren Sie C’tor, Zuweisung kopieren, Zuweisen verschieben oder Destruktorund es ist nicht als gekennzeichnet gelöscht und alle Mitglieder sind beweglich. Ähnlich für Zuweisen verschieben: Wird generiert, wenn kein Benutzer definiert ist Kopieren Sie C’tor, Kopieren Sie C’tor, Zuweisen verschieben oder Destruktorund es ist nicht als gekennzeichnet gelöscht und alle Mitglieder sind beweglich. Beachten Sie, dass diese Regeln etwas ausgefeilter sind als die C++03-Regeln und in der Praxis sinnvoller sind.

    – towi

    24. November 2013 um 12:38 Uhr

Meinst du „definiert“ mit „erstellt“?

$12.1 – “Der Standardkonstruktor (12.1), der Kopierkonstruktor und der Kopierzuweisungsoperator (12.8) und der Destruktor (12.4) sind spezielle Elementfunktionen.

Wenn „erstellt“ „definiert“ bedeutet, dann sind hier die wichtigen Teile des C++-Standards.

-Ein implizit deklarierter Standardkonstruktor für eine Klasse wird implizit definiert, wenn er zum Erstellen eines Objekts seines Klassentyps (1.8) verwendet wird.

-Wenn eine Klasse keinen vom Benutzer deklarierten Destruktor hat, wird implizit ein Destruktor deklariert. Ein implizit deklarierter Destruktor wird implizit definiert, wenn er verwendet wird, um ein Objekt seines Klassentyps zu zerstören.

-Wenn die Klassendefinition keinen Kopierkonstruktor explizit deklariert, wird einer implizit deklariert. Ein implizit deklarierter Kopierkonstruktor ist implizit definiert, wenn er verwendet wird, um ein Objekt seines Klassentyps aus einer Kopie eines Objekts seines Klassentyps oder eines von seinem Klassentyp abgeleiteten Klassentyps zu initialisieren.

– Wenn die Klassendefinition keinen Kopierzuweisungsoperator explizit deklariert, wird implizit einer deklariert. Ein implizit deklarierter Kopierzuweisungsoperator wird implizit definiert, wenn einem Objekt seines Klassentyps ein Wert seines Klassentyps oder ein Wert eines von seinem Klassentyp abgeleiteten Klassentyps zugewiesen wird.

C++17 N4659-Standardentwurf

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1 “Erklärungen und Definitionen” hat eine Anmerkung, die wahrscheinlich alle zusammenfasst:

3
[ Note: In some circumstances, C ++ implementations implicitly define the default constructor (15.1), copy
constructor (15.8), move constructor (15.8), copy assignment operator (15.8), move assignment operator (15.8),
or destructor (15.4) member functions. — end note ] [ Example: Given

#include <string>

struct C {
  std::string s;         // std::string is the standard library class (Clause 24)
};

int main() {
  C a;
  C b = a;
  b = a;
}

the implementation will implicitly define functions to make the definition of C equivalent to

struct C {
  std::string s;
  C() : s() { }
  C(const C& x): s(x.s) { }
  C(C&& x): s(static_cast<std::string&&>(x.s)) { }
  // : s(std::move(x.s)) { }
  C& operator=(const C& x) { s = x.s; return *this; }
  C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
  // { s = std::move(x.s); return *this; }
  ~ C() { }
};

— end example ]

Die Bedingungen, unter denen diese deklariert werden, werden erläutert unter: Bedingungen für die automatische Generierung von Default/Copy/Move-Ctor und Copy/Move-Zuweisungsoperator?

Eine coole Möglichkeit, um sicherzustellen, dass etwas einen Standardwert hat, besteht darin, zu versuchen, ihn zu verwenden = default wie erklärt unter: Was bedeutet “Standard” nach der Funktionsdeklaration einer Klasse?

Das folgende Beispiel tut dies und übt auch alle implizit definierten Funktionen aus.

#include <cassert>
#include <string>

struct Default {
    int i;
    Default()                          = default;
    Default(const Default&)            = default;
    Default& operator=(Default&)       = default;
    Default& operator=(const Default&) = default;
    Default(Default&&)                 = default;
    Default& operator=(Default&&)      = default;
    ~Default()                         = default;
};

struct Instrument {
    int i;
    static std::string last_call;
    Instrument()                             { last_call = "ctor"; }
    Instrument(const Instrument&)            { last_call = "copy ctor"; }
    Instrument& operator=(Instrument&)       { last_call = "copy assign"; return *this; }
    Instrument& operator=(const Instrument&) { last_call = "copy assign const"; return *this; }
    Instrument(Instrument&&)                 { last_call = "move ctor";  }
    Instrument& operator=(Instrument&&)      { last_call = "move assign"; return *this; }
    ~Instrument()                            { last_call = "dtor"; }
};
std::string Instrument::last_call;

int main() {
    // See what the default constructors are doing.
    {
        // Default constructor.
        Default ctor;
        // i is uninitialized.
        // std::cout << ctor.i << std::endl;
        ctor.i = 1;

        // Copy constructor.
        Default copy_ctor(ctor);
        assert(copy_ctor.i = 1);

        // Copy assignment.
        Default copy_assign;
        copy_assign = ctor;
        assert(copy_assign.i = 1);

        // Copy assignment const.
        const Default const_ctor(ctor);
        Default copy_assign_const;
        copy_assign_const = const_ctor;
        assert(copy_assign_const.i == 1);

        // Move constructor.
        Default move_ctor(std::move(ctor));
        assert(move_ctor.i == 1);

        // Move assignment.
        Default move_assign;
        move_assign = std::move(ctor);
        assert(move_assign.i == 1);
    }

    // Check that the constructors are called by these calls.
    {
        // Default constructor.
        Instrument ctor;
        assert(Instrument::last_call == "ctor");

        // Copy constructor.
        Instrument copy_ctor(ctor);
        assert(Instrument::last_call == "copy ctor");

        // Copy assignment.
        copy_ctor = ctor;
        assert(Instrument::last_call == "copy assign");

        // Copy assignment const.
        const Instrument const_ctor(ctor);
        Instrument copy_assign_const;
        copy_assign_const = const_ctor;
        assert(Instrument::last_call == "copy assign const");

        // Move constructor.
        Instrument move_ctor(std::move(ctor));
        assert(Instrument::last_call == "move ctor");

        // Move assignment.
        Instrument move_assign;
        move_assign = std::move(ctor);
        assert(Instrument::last_call == "move assign");

        // Destructor.
        {
            Instrument dtor;
        }
        assert(Instrument::last_call == "dtor");
    }
}

GitHub-Upstream.

Getestet mit GCC 7.3.0:

g++ -std=c++11 implicitly_defined.cpp

Was sind alle Member Funktionen die vom Compiler fur eine Klasse
Anspruch

Wenn nicht vom Benutzer implementiert, fügt der Compiler standardmäßig einige Elementfunktionen zur Klasse hinzu. Das sind die großen Vier:

  • Standardkonstruktor
  • Konstruktor kopieren
  • Kopieroperator (Zuweisung)
  • Zerstörer

Abhängig von den Typen der Mitglieder und der von Ihnen selbst bereitgestellten Mitgliederfunktion werden diese nicht alle generiert.

Andere Antworten haben Ihnen gesagt, was erstellt wird und dass der Compiler sie möglicherweise nur generiert, wenn er verwendet wird.

Meine Sorge ist, ob es für alle Klassen erstellt wird …

Warum besorgt? Denken Sie, dass es unerwünschten Code in der ausführbaren Datei erstellt? Unwahrscheinlich, aber Sie können es leicht mit Ihrer Umgebung überprüfen.

Oder vielleicht war Ihre Sorge, dass es keinen Konstruktor erstellen könnte, wenn Sie einen wollen? Kein Grund zur Sorge … sie werden immer bei Bedarf erstellt und nicht vom Benutzer bereitgestellt.

… und warum wird der Standardkonstruktor benötigt?

Weil Klassen Objekte mit ihren eigenen Destruktoren enthalten können, die systematisch aufgerufen werden müssen. Zum Beispiel gegeben…

struct X
{
    std::string a;
    std::string b;
};

…der Standarddestruktor sorgt dafür, dass die Destruktoren für a und b laufen.

  • Nein, die Destruktoren für b und a wird automatisch ausgeführt nach dem Leer laufen X Zerstörer.

    – fredoverflow

    17. September 2010 um 10:11 Uhr

  • @Fred: Aus Sicht des Benutzercodes wahr. Aber der Compiler verkettet alle expliziten X-Zerstörungskörper (falls vorhanden) mit den Unterobjekt-Destruktoren, um die eigentliche Destruktorfunktion zu bilden.

    – Toni Delroy

    20. September 2010 um 13:52 Uhr

  • Nein, die Destruktoren für b und a wird automatisch ausgeführt nach dem Leer laufen X Zerstörer.

    – fredoverflow

    17. September 2010 um 10:11 Uhr

  • @Fred: Aus Sicht des Benutzercodes wahr. Aber der Compiler verkettet alle expliziten X-Zerstörungskörper (falls vorhanden) mit den Unterobjekt-Destruktoren, um die eigentliche Destruktorfunktion zu bilden.

    – Toni Delroy

    20. September 2010 um 13:52 Uhr

988760cookie-checkWas sind alle Member-Funktionen, die vom Compiler für eine Klasse erstellt werden? Passiert das ständig?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy