Wann wird Zuweisungsoperator aufgerufen und wann der Copy C'tor ?



  • Die abgeleitete Klasse hat einen, aber die Basisklasse nicht:
    - Basisklassenobjekt wird durch eine bitweise Kopie initialisiert, aber nur dann, wenn im Copy-Konstruktor der abgeleiteten Klasse explizit der Copy-Konstruktor der Basisklasse aufgerufen (Initialisierungsliste) wird. Ansonsten wird der Standardkonstruktor der Basisklasse aufgerufen.
    - Das Objekt der abgeleiteten Klasse wird durch den Copy-Konstruktor initialisiert

    Wie soll ich denn den Copy Konstruktor explizit aufrufen wenn ich gar keinen definiert habe ? 😕
    Generiert mir der Compiler also einen Standard Copy Konstruktor ?

    abgeleitete Klasse hat einen, aber die Basisklasse nicht



  • Es gibt 4 Dinge, die jede Klasse immer hat:

    1. Defaul-Konstruktor
    2. Copy-Konstruktor
    3. Destruktor
    4. Zuweisungsoperator

    Falls du diese nicht explizit definierst, generiert sie der Compiler, wobei der Rumpf dieser spziellen Methoden dann natürlich leer ist.

    PS: In meinem vorherigen Post habe ich mit "er besitzt einen" natürlich einen explizit definierten gemeint.

    lg



  • Bacid90210 schrieb:

    Heißt das jetzt dass wenn ich beide Copy Konstruktoren implementiert habe im Copy C'tor der abgeleiteten Klasse den Copy C'tor der Oberklasse nicht explizit aufrufen muss ? Ja oder ... ?

    Richtig, es wird automatisch gemacht... es wird immer zuerst das Objekt der Basisklasse erstellt und danach das Objekt der abgeleiteten Klasse. Beim Destruktor ist es umgekehrt, da wird immer zuerst das Objekt der abgeleiteten Klasse zerstört, und dann das der Basisklasse.

    lg



  • @Gugelmoser:

    Aua aua aua.

    Man kann - bis auf den Destruktor - alle von dir aufgeführten Dinge unterdrücken, so dass eine Klasse nichts davon hat.
    Es ist auch nicht unüblich das zu machen, z.B. um Objekte unkopierbar zu machen (siehe boost::noncopyable).

    Und bei RAII haste oft Klassen die man nicht Default-Instanzieren kann (=die nur Konstruktoren haben die Parameter brauchen).
    RAII Klassen sind auch meistens gleichzeitig "noncopyable", d.h. die haben dann alle 3 von dir genannten Dinge nicht.

    Weder Basisklasse noch abgeleitete Klasse besitzen einen Copy-Konstruktor:
    Basisklassenobjekt und das Objekt der abgeleiteten Klasse werden durch eine bitweise Kopie initialisiert.

    NEIN,
    in dem Fall haben die Basisklasse und die abgeleitete Klasse einen implizit definierten Konstruktor, der wiederum alle implizit definierten Konstruktoren der Basisklassen und Member aufruft.
    Soll heissen, hier wird nix bitweise Kopiert (wäre auch fatal):

    class Base
    {
        std::string m_a_string;
    };
    class Derived : public Base
    {
        std::string m_another_string;
    };
    


  • hustbaer schrieb:

    Man kann - bis auf den Destruktor - alle von dir aufgeführten Dinge unterdrücken, so dass eine Klasse nichts davon hat.

    Ok, da würde mich nun interessieren wie man das genau anstellt?

    hustbaer schrieb:

    NEIN,
    in dem Fall haben die Basisklasse und die abgeleitete Klasse einen implizit definierten Konstruktor, der wiederum alle implizit definierten Konstruktoren der Basisklassen und Member aufruft.
    Soll heissen, hier wird nix bitweise Kopiert (wäre auch fatal):

    Ok, das habe ich nun aus dem Vorlesungs-Skript meines Professors. Gut zu wissen dass das Quatsch ist :). Gibt es den dann überhaupt in irgendeinem Fall ein bitweises kopieren? Und wieso wäre das fatal?

    lg



  • Gugelmoser schrieb:

    hustbaer schrieb:

    Man kann - bis auf den Destruktor - alle von dir aufgeführten Dinge unterdrücken, so dass eine Klasse nichts davon hat.

    Ok, da würde mich nun interessieren wie man das genau anstellt?

    Indem du beispielsweise den CCTOR und copy-asignment-op einfach privat deklarierst.

    Gugelmoser schrieb:

    hustbaer schrieb:

    NEIN,
    in dem Fall haben die Basisklasse und die abgeleitete Klasse einen implizit definierten Konstruktor, der wiederum alle implizit definierten Konstruktoren der Basisklassen und Member aufruft.
    Soll heissen, hier wird nix bitweise Kopiert (wäre auch fatal):

    Ok, das habe ich nun aus dem Vorlesungs-Skript meines Professors. Gut zu wissen dass das Quatsch ist :). Gibt es den dann überhaupt in irgendeinem Fall ein bitweises kopieren? Und wieso wäre das fatal?

    lg

    Das string-Objekt hält beispielsweise einen pointer auf einen Speicherbereich wo die Zeichenkette abgespeichert wird.
    Würdest du nun bitweise kopieren, würdest du einfach die Speicheradresse kopieren, nicht aber die verwaltete Zeichenkette. Ändert dann Objekt1 die Zeichenkette, würde Objekt2 ebenfalls etwas von der Änderung mitbekommen (zeigt ja auf den gleichen Speicherbereich) - oder Objekt1 wird gelöscht - was ist dann mit Objekt2?



  • inter2k3 schrieb:

    Gugelmoser schrieb:

    hustbaer schrieb:

    Man kann - bis auf den Destruktor - alle von dir aufgeführten Dinge unterdrücken, so dass eine Klasse nichts davon hat.

    Ok, da würde mich nun interessieren wie man das genau anstellt?

    Indem du beispielsweise den CTOR und copy-asignment-op einfach privat deklarierst.

    Oder -zumindest für die Konstruktoren- einfach einen Konstruktor mit Parameter definierst.



  • inter2k3 schrieb:

    Gugelmoser schrieb:

    hustbaer schrieb:

    Man kann - bis auf den Destruktor - alle von dir aufgeführten Dinge unterdrücken, so dass eine Klasse nichts davon hat.

    Ok, da würde mich nun interessieren wie man das genau anstellt?

    Indem du beispielsweise den CTOR und copy-asignment-op einfach privat deklarierst.

    Naja, dann kann ich aber immernoch innerhalb der Klasse darauf zugreifen. Von dem her würd ich sowas nicht als "unterdrücken" bezeichnen, schließlich sind sie ja immernoch da...

    lg



  • @Jockelx: Das verhindert ja nicht das Generieren eines CCTORS oder assignment-ops, sondern nur das Generieren eines def. CTORS.

    @Gugelmoser: Nein - sie sind ja nur deklariert, nicht definiert.



  • inter2k3 schrieb:

    @Gugelmoser: Nein - sie sind ja nur deklariert, nicht definiert.

    achso, so hast du das gemeint. Gut, dann hat man aber einen Linker-Error, falls man versucht, innerhalb der Klasse darauf zuzugreifen. Wenn ich nun von einem Programm ausgehe, das weder Syntax- noch Linker-Fehler aufweißt... hab ich dann nicht immer die "Großen Drei"?

    lg



  • Naja, du kannst sie ja auch definieren und lässt einfach die Initialisierungsliste weg bzw. den Rumpf leer.



  • inter2k3 schrieb:

    Das string-Objekt hält beispielsweise einen pointer auf einen Speicherbereich wo die Zeichenkette abgespeichert wird.
    Würdest du nun bitweise kopieren, würdest du einfach die Speicheradresse kopieren, nicht aber die verwaltete Zeichenkette. Ändert dann Objekt1 die Zeichenkette, würde Objekt2 ebenfalls etwas von der Änderung mitbekommen (zeigt ja auf den gleichen Speicherbereich) - oder Objekt1 wird gelöscht - was ist dann mit Objekt2?

    Gut du redest nun von deep-copy oder? deep-copy hat man aber nur, wenn man entsprechend explizit definiert. Das vom Compiler generierte macht nur eine flat-copy (das Objekt hinter dem Zeiger wird nich kopiert).



  • Wenn ich nun von einem Programm ausgehe, das weder Syntax- noch Linker-Fehler aufweißt... hab ich dann nicht immer die "Großen Drei"?

    Nö.

    Üblicherweise verwendet man ein Hilfsmember oder eine Hilfsbasisklasse um die drei genannten Dinge zu unterdrücken.
    Beispiel:

    class NoCopyHelper
    {
    private:
        NoCopyHelper(NoCopyHelper const&) {} // darf ruhig definiert sein, stört nicht (muss aber nicht definiert sein, bringt auch nix)
    };
    
    class TrulyNonCopyable : private NoCopyHelper
    {
    //    NoCopyHelper m_dummy; // würde auch gehen
    };
    

    TrulyNonCopyable hat jetzt wirklich gar keinen Copy-Ctor mehr, nichtmal nen privaten.

    p.S.: für den Assignment-Operator reicht es schon wenn du ein "const" Member in der Klasse hast. Dann kann der auch nimmer implizit definiert werden.



  • Gugelmoser schrieb:

    inter2k3 schrieb:

    Das string-Objekt hält beispielsweise einen pointer auf einen Speicherbereich wo die Zeichenkette abgespeichert wird.
    Würdest du nun bitweise kopieren, würdest du einfach die Speicheradresse kopieren, nicht aber die verwaltete Zeichenkette. Ändert dann Objekt1 die Zeichenkette, würde Objekt2 ebenfalls etwas von der Änderung mitbekommen (zeigt ja auf den gleichen Speicherbereich) - oder Objekt1 wird gelöscht - was ist dann mit Objekt2?

    Gut du redest nun von deep-copy oder? deep-copy hat man aber sowieso nur, wenn man selbst explizit definiert. Das vom Compiler generierte macht nur eine flat-copy.

    Das ist schon klar - aber genau deshalb ist es ja wichtig, dass eben keine bitweise Kopie erstellt wird, sondern die Konstruktoren der member aufgerufen werden.

    Wenn deine eigene Klasse einen Zeiger auf Heapspeicher hält, dann musst du natürlich dafür sorgen, dass der Inhalt des Speichers auch kopiert wird.

    Wenn du allerdings wie beim obigen Beispiel einfach ein member hast, welches einen Zeiger auf Heapspeicher hat, dann wäre es jetzt schön doof wenn das member bitweise kopiert wird anstelle des Konstruktoraufrufs (der wäre ja dann für die Katz, aber gerade der würde für die deep-copy sorgen).



  • hustbaer schrieb:

    Üblicherweise verwendet man ein Hilfsmember oder eine Hilfsbasisklasse um die drei genannten Dinge zu unterdrücken.
    Beispiel:

    class NoCopyHelper
    {
    private:
        NoCopyHelper(NoCopyHelper const&) {} // darf ruhig definiert sein, stört nicht (muss aber nicht definiert sein, bringt auch nix)
    };
    
    class TruelyNonCopyable : private NoCopyHelper
    {
    //    NoCopyHelper m_dummy; // würde auch gehen
    };
    

    ah, ok :), aber kann man mit der Klasse TruelyNonCopyable jetzt noch etwas anfangen?

    lg



  • Freund Gugelmoser, hast du wirklich so wenig Phantasie?

    Viele meiner Klassen sind non-copyable. Denk an Fenster/Widgets, Files, Socket-Connections oder Mutexen.
    Bzw. auch Klassen für die es zwar eine naheliegende Kopiersemantik gibt, die aber eigentlich nie kopiert werden müssen, und wo der Copy-Ctor einigermassen aufwendig zu implementieren wäre.

    EDIT: scheissendreck, truly schreibt man ohne e! 😃


Anmelden zum Antworten