Zuweisungsoperator mit const Attributen



  • Hey,

    ich arbeite gerade mit Objekten die "const" Attribute haben und benötige nun einen Zuweisungsoperator. Der hier im Tutorial so angegeben wird:

    X& X::operator= (X const& rhs)
    {
      if (this != &rhs)  //oder if (*this != rhs)
      {
        /* kopiere elementweise, oder:*/
        X tmp(rhs); //Copy-Konstruktor
        swap(tmp);
      }
      return *this; //Referenz auf das Objekt selbst zurückgeben
    }
    

    Nun hänge ich bei der swap Methode, da mir zwar der Sinn (glaube ich) soweit klar ist, ich aber keine Vorstellung habe wie ich diese implementieren soll.

    Es wäre nett wenn mir da jemand eine Erläuterung zu geben könnte.



  • Eine swap-Methode tauscht die Elemente des Arguments mit den Elementen des Objektes aus, auf dem sie aufgerufen wird. Ob sowas bei deiner Konstellation sinnvoll ist, ist die Frage. Grade wenn du const Elemente hast, ist es sowieso fraglich, ob ein op= sinnig ist.
    Ohne deine Klasse zu kennen kann man da aber nicht viel sagen...



  • class Order {
    
    public:
        //das Land das den Befehl macht
        const Staaten wer;
        //der Befhl
        const Befehl was;
        //von wo der Befehl ist
        const std::string von;
        //nach wo der Befehl ist
        const std::string nach;
        //zeile in der datei
        const int zeile;
    
        Order(Staaten wr,Befehl ws,std::string vn,std::string nch,int zele):wer(wr),was(ws),von(vn),nach(nch),zeile(zele) {};
    
        Order(const Order& e) : wer(e.wer),was(e.was),von(e.von),nach(e.nach),zeile(e.zeile) {};
    
        Order& operator=(Order const& rhs) //unfertiger operator
        {
            if(this != rhs) {
            Order(rhs);
    
            }
            return *this;
        }
    };
    

    Das wär die Klasse (soweit), das Problem mit dem op= stellt sich aufgrund der Verwendung von Vectoren (wenn ich das recht überreiße).

    ./logik/parser/../order/Order.hpp:18:   instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = Order, _Alloc = std::allocator<Order>]’
    /usr/include/c++/4.4/bits/stl_vector.h:741:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = Order, _Alloc = std::allocator<Order>]’
    ./logik/parser/Parser.cpp:48:   instantiated from here
    ./logik/parser/../order/Order.hpp:18: error: non-static const member ‘const Staaten Order::wer’, can't use default assignment operator
    ./logik/parser/../order/Order.hpp:18: error: non-static const member ‘const Befehl Order::was’, can't use default assignment operator
    ./logik/parser/../order/Order.hpp:18: error: non-static const member ‘const std::string Order::von’, can't use default assignment operator
    ./logik/parser/../order/Order.hpp:18: error: non-static const member ‘const std::string Order::nach’, can't use default assignment operator
    ./logik/parser/../order/Order.hpp:18: error: non-static const member ‘const int Order::zeile’, can't use default assignment operator
    

    Beim letzten mal habe ich es mit einer Überladung des op= gelöst, allerdings war ich da nicht sonderlich zufrieden mit meiner Lösung und wollte nun die korrekte Anwenden.



  • Natürlich kannst du Konstanten nichts zuweisen. Lass das const vor den Membervariablen weg, wenn du Zuweisung benötigst, alles andere ist Murks.

    Oder du verzichtest auf Wertsemantik und machst Kopierkonstruktor und Zuweisungsoperator privat. Dann kannst du deine Objekte aber nicht mehr direkt in std::vector speichern, dafür z.B. in boost::ptr_vector .



  • ich will ja nichts zuweisen, ich habe mir schon gut überlegt wieso ich da const verwende, die Werte müssen unabänderlich sein um die Konsistenz des Programms zu gewährleisten. Eine Zuweisung von meiner Seite aus findet eigtl. nie statt. Ich hatte aus dem Fehler mehr interpretiert das die Vectoren intern einen solchen Murks machen.

    aber schon mal danke für deine Hilfe



  • Jud4s schrieb:

    ich will ja nichts zuweisen, ich habe mir schon gut überlegt wieso ich da const verwende, die Werte müssen unabänderlich sein um die Konsistenz des Programms zu gewährleisten. Eine Zuweisung von meiner Seite aus findet eigtl. nie statt. Ich hatte aus dem Fehler mehr interpretiert das die Vectoren intern einen solchen Murks machen.

    aber schon mal danke für deine Hilfe

    Wenn du nichts zuweisen willst, wofür brauchst du dann den Zuweisungsoperator?

    Edit:
    Ah, du willst die Dinger in einem vector ablegen, dazu solltest du aber wissen, wie vector intern funktioniert. Alle Container der STL arbeiten intern mit Kopien, d.h. es werden Kopien der übergebenen Objekte im Container abgelegt. Wenn diese Objekte allerdings keine Kopien zulassen kannst du sie nicht ohne weiteres in einem Container ablegen. Du könntest sie auf dem Heap erzeugen, in einen smart_ptr verpacken und in einem Container ablegen, einen pointer_container aus den boost Bibliotheken benutzen oder dir überlegen, ob du dein Design nicht anpassen solltest. Wenn du Member Variablen const deklarierst passt irgendwas mit der Kapselung nicht, wer sollte die denn manipulieren wollen?

    Edit, die Zweite:
    Mach die member private und stell entsprechende Zugriffsmethoden zur Verfügung, sowas wie

    #include <string>
    
    class DingsBums
    {
    private:
      std::string DingensKirchen_;
    public:
      const std::string& DingensKirchen() const
      {
         return DingensKirchen_;
      }
    }
    

    dann klappt auch mit dem Nachbarn vector.



  • Weil der Vector, bzw der Compiler den verlangt:

    €:Das gehörte noch zu der obigen Fehlerliste, hatte anscheinend zu früh kopiert.

    /usr/include/c++/4.4/bits/vector.tcc:312: note: synthesized method ‘Order& Order::operator=(const Order&)’ first required here
    


  • Wenn du Member Variablen const deklarierst passt irgendwas mit der Kapselung nicht, wer sollte die denn manipulieren wollen?

    Ich hatte gelesen das die Verwendung von const es dem Compiler erleichtert Einsparungen zu machen.
    Des weiteren ich bin mit der Logik dran gegangen "was nicht geändert werden darf, darf auch später nicht geändert werden", da ich aber auch keine Lust hatte unlesbaren Code zu erzeugen, wollte ich auf Getter verzichten und mit dem Objekt umspringen wie mit einem Struct, direkter Attribute Zugriff , über das deklarieren als const schien mir das dann auch ausreichend abgesichert.

    €: Welchen genaueren Sinn soll denn const sonst erfüllen, ausser zu kennzeichnen, das ein Attribut/eine Variable unabänderlich ist?



  • Jud4s schrieb:

    €: Welchen genaueren Sinn soll denn const sonst erfüllen, ausser zu kennzeichnen, das ein Attribut/eine Variable unabänderlich ist?

    Keinen. Aber du solltest nicht den Umkehrschluss ziehen und denken, alles, was sich nicht ändert, müsse const sein. Damit machst du dir in deinem Fall die Wertsemantik kaputt.

    Nochmals: Entweder du verzichtest hier auf const und kapselst deine Klasse vernünftig, oder du verzichtest auf die Speicherung als Werte in STL-Containern.

    Jud4s schrieb:

    Ich hatte gelesen das die Verwendung von const es dem Compiler erleichtert Einsparungen zu machen.

    Vergiss das am besten. Wenn du const auf diese Weise verwendest, musst du dich über Fehler nicht wundern.



  • Danke, ich denke ich werde meine Datenkapselung einfach erneut überdenken müssen.



  • Zwar nicht ganz zum Thema passend, aber zur Frage "Wie implementiere ich den operator = korrekt":

    T& operator = (T other)
    {
        swap(other);
        return *this;
    }
    

    .) Exception-save
    .) Kann sowohl kopieren, als auch moven
    .) Einfach
    .) Performant



  • Edit:
    Narf, da hab ich doch tatsächlich übersehen, dass du den Parameter per value übergibst und damit schon ´ne Kopie erstellt hast. Hab den Rest meines Beitrags mal gelöscht 😃



  • Schau nochmal genau hin. T wird hier per Value genommen und dementsprechend kopiert oder gemoved und am Ende zerstört.



  • 314159265358979 schrieb:

    Schau nochmal genau hin. T wird hier per Value genommen und dementsprechend kopiert oder gemoved und am Ende zerstört.

    Jau, grad gesehen.



  • Bei einem grossen Datentyp ohne COW kann es aber nicht mehr so performant aussehen.



  • Zeig mir einen performanteren :p


  • Mod

    314159265358979 schrieb:

    .) Performant

    Das kommt darauf an. Nämlich darauf, ob nicht eventuell bereits verwendete Resourcen wiederverwendet werden können und somit evtl. etwas gespart werden kann.

    Betrachte z.B.

    vector<int> a(1000,0),b(100,0);
    a = b;
    

    Es ist mir nicht ganz klar, ob vector hier copy&swap durchführen darf (mit entsprechenden Folgen für capacity), jedenfalls wird es nicht vorgeschrieben und wäre u.U. ineffizient.
    copy&swap ist zwar exception-safe, andererseits tritt in diesem Beispiel garantiert keine Exception auf, wenn direkt zugewiesen wird.



  • Das hängt aber davon ab, was der Copy Ctor macht.


Log in to reply