77 Stimmen

Warum funktioniert der Operator = bei Strukturen, die nicht definiert wurden?

Schauen wir uns ein einfaches Beispiel an:

struct some_struct {
   std::string str;
   int a, b, c;
}

some_struct abc, abc_copy;
abc.str = "some text";
abc.a = 1;
abc.b = 2;
abc.c = 3;

abc_copy = abc;

Dann ist abc_copy eine exakte kopieren. de abc wie ist es möglich sans Definition der =-Operator ?

(Das hat mich bei der Arbeit an einem Code überrascht )

120voto

Martin York Punkte 245363

Wenn Sie diese vier Methoden (sechs in C++11) nicht definieren, erzeugt der Compiler sie für Sie:

  • Standard-Konstruktor
  • Konstruktor kopieren
  • Zuweisung Operator
  • Zerstörer
  • Move-Konstruktor (C++11)
  • Zuweisung verschieben (C++11)

Wenn Sie wissen wollen, warum?
Damit soll die Abwärtskompatibilität mit C gewahrt werden (da C-Strukturen mit = und in der Deklaration kopierbar sind). Aber es macht auch das Schreiben einfacher Klassen einfacher. Einige würden argumentieren, dass es wegen des "shallow copy problem" zu Problemen führt. Mein Argument dagegen ist, dass man keine Klasse mit eigenen RAW-Zeigern in ihr haben sollte. Durch die Verwendung geeigneter Smart Pointer wird dieses Problem beseitigt.

Standard-Konstruktor (wenn keine anderen Konstruktoren definiert sind)

Der vom Compiler erzeugte Standardkonstruktor ruft den Standardkonstruktor der Basisklasse und dann den Standardkonstruktor jedes Mitglieds auf (in der Reihenfolge, in der sie deklariert sind)

Destruktor (wenn kein Destruktor definiert ist)

Ruft den Destruktor der einzelnen Mitglieder in umgekehrter Reihenfolge der Deklaration auf. Ruft dann den Destruktor der Basisklasse auf.

Kopierkonstruktor (wenn kein Kopierkonstruktor definiert ist)

Ruft den Kopierkonstruktor der Basisklasse auf und übergibt das src-Objekt. Ruft dann den Kopierkonstruktor jedes Mitglieds auf und verwendet dabei die Mitglieder des src-Objekts als den zu kopierenden Wert.

Zuweisung Operator

Ruft den Zuweisungsoperator der Basisklasse auf und übergibt das src-Objekt. Ruft dann den Zuweisungsoperator für jedes Mitglied auf und verwendet das src-Objekt als den zu kopierenden Wert.

Move-Konstruktor (wenn kein Move-Konstruktor definiert ist)

Ruft den Move-Konstruktor der Basisklasse auf und übergibt das src-Objekt. Ruft dann den move-Konstruktor jedes Mitglieds auf und verwendet die Mitglieder des src-Objekts als den zu verschiebenden Wert.

Zuweisungsoperator verschieben

Ruft den Zuweisungsoperator der Basisklasse move auf und übergibt das Objekt src. Ruft dann den Zuweisungsoperator für jedes Mitglied auf und verwendet dabei das src-Objekt als den zu kopierenden Wert.

Wenn Sie eine Klasse wie diese definieren:

struct some_struct: public some_base
{   
    std::string str1;
    int a;
    float b;
    char* c;
    std::string str2;
};

Was der Compiler erstellt, ist:

struct some_struct: public some_base
{   
    std::string str1;
    int a;
    float b;
    char* c;
    std::string str2;

    // Conceptually two different versions of the default constructor are built
    // One is for value-initialization the other for zero-initialization
    // The one used depends on how the object is declared.
    //        some_struct* a = new some_struct;     // value-initialized
    //        some_struct* b = new some_struct();   // zero-initialized
    //        some_struct  c;                       // value-initialized
    //        some_struct  d = some_struct();       // zero-initialized
    // Note: Just because there are conceptually two constructors does not mean
    //       there are actually two built.

    // value-initialize version
    some_struct()
        : some_base()            // value-initialize base (if compiler generated)
        , str1()                 // has a normal constructor so just call it
        // PODS not initialized
        , str2()
   {}

    // zero-initialize version
    some_struct()
        : some_base()            // zero-initialize base (if compiler generated)
        , str1()                 // has a normal constructor so just call it.
        , a(0)
        , b(0)
        , c(0)   // 0 is NULL
        , str2()
        // Initialize all padding to zero
   {}

    some_struct(some_struct const& copy)
        : some_base(copy)
        , str1(copy.str1)
        , a(copy.a)
        , b(copy.b)
        , c(copy.c)
        , str2(copy.str2)
    {}

    some_struct& operator=(some_struct const& copy)
    {
        some_base::operator=(copy);
        str1 = copy.str1;
        a    = copy.a;
        b    = copy.b;
        c    = copy.c;
        str2 = copy.str2;
        return *this;
    }

    ~some_struct()
    {}
    // Note the below is pseudo code
    // Also note member destruction happens after user code.
    // In the compiler generated version the user code is empty
        : ~str2()
        // PODs don't have destructor
        , ~str1()
        , ~some_base();
    // End of destructor here.

    // In C++11 we also have Move constructor and move assignment.
    some_struct(some_struct&& copy)
                    //    ^^^^  Notice the double &&
        : some_base(std::move(copy))
        , str1(std::move(copy.str1))
        , a(std::move(copy.a))
        , b(std::move(copy.b))
        , c(std::move(copy.c))
        , str2(std::move(copy.str2))
    {}

    some_struct& operator=(some_struct&& copy)
                               //    ^^^^  Notice the double &&
    {
        some_base::operator=(std::move(copy));
        str1 = std::move(copy.str1);
        a    = std::move(copy.a);
        b    = std::move(copy.b);
        c    = std::move(copy.c);
        str2 = std::move(copy.str2);
        return *this;
    } 
};

8voto

MHarris Punkte 1781

In C++ entsprechen Structs Klassen, bei denen die Mitglieder standardmäßig öffentlich und nicht privat zugänglich sind.

C++-Compiler erzeugen auch die folgenden speziellen Mitglieder einer Klasse automatisch, wenn sie nicht angegeben sind:

  • Standard-Konstruktor - keine Argumente, standardmäßig wird alles initalisiert.
  • Konstruktor kopieren - d.h. eine Methode mit dem gleichen Namen wie die Klasse, die einen Verweis auf ein anderes Objekt derselben Klasse annimmt. Kopiert alle Werte hinüber.
  • Zerstörer - Wird aufgerufen, wenn das Objekt zerstört wird. Macht standardmäßig nichts.
  • Zuweisungsoperator - Wird aufgerufen, wenn eine Struktur/Klasse einer anderen zugewiesen wird. Dies ist die automatisch generierte Methode, die im obigen Fall aufgerufen wird.

5voto

Ferruccio Punkte 96076

Dieses Verhalten ist notwendig, um die Quellkompatibilität mit C zu erhalten.

C bietet keine Möglichkeit, Operatoren zu definieren oder zu überschreiben, daher werden Strukturen normalerweise mit dem =-Operator kopiert.

4voto

eran Punkte 21116

Aber sie ist definiert. In der Norm. Wenn Sie keinen Operator = angeben, wird Ihnen einer geliefert. Und der Standardoperator kopiert einfach jede der Mitgliedsvariablen. Und woher weiß er, auf welche Weise er die einzelnen Elemente kopieren soll? Er ruft deren Operator = auf (der, wenn er nicht definiert ist, standardmäßig geliefert wird...).

3voto

Sam Harwell Punkte 94511

En Zuweisungsoperator ( operator= ) ist eine der implizit erzeugten Funktionen für eine Struktur oder Klasse in C++.

Hier ist ein Verweis, der die 4 implizit erzeugten Mitglieder beschreibt:
http://www.cs.ucf.edu/~leavens/larchc++handbuch/lcpp_136.html

Kurz gesagt, das implizit erzeugte Mitglied führt eine mitgliederweise flache Kopie . Hier ist die Langfassung der verlinkten Seite:

Die implizit generierte Spezifikation des Zuweisungsoperators ist, falls erforderlich, die folgende. Die Spezifikation besagt, dass das Ergebnis das zugewiesene Objekt ist ( self ), und dass der Wert des abstrakten Wertes von self im Nachstadium self "ist derselbe wie der Wert des abstrakten Werts des Arguments from .

// @(#)$Id: default_assignment_op.lh,v 1.3 1998/08/27 22:42:13 leavens Exp $
#include "default_interfaces.lh"

T& T::operator = (const T& from) throw();
//@ behavior {
//@   requires assigned(from, any) /\ assigned(from\any, any);
//@   modifies self;
//@   ensures result = self /\ self" = from\any\any;
//@   ensures redundantly assigned(self, post) /\ assigned(self', post);
//           thus
//@   ensures redundantly assigned(result, post) /\ assigned(result', post);
//@ }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X