Regel der Großen Drei aushebeln?!



  • Hallo zusammen,

    die Regel der großen Drei besagt, dass man, falls man einen der 3 selbst definiert auch auf jedenfall die anderen beiden selbst definieren muss.
    Ich spreche natürlich von Kopierkonstruktor, Zuweisungsoperator und Destruktor.

    Aber wird diese Regeln nicht von Smartpointern ausgehebelt, unter gewissen Umständen?!

    Folgendes Beispiel dazu:

    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <memory>
    
    class Base
    {
    	int dummy;
    
    public:
    	virtual std::unique_ptr<Base> clone() const = 0;
    };
    
    class DummyDerive : public Base
    {
    	float dummies[10];
    
    public:
    	virtual std::unique_ptr<Base> clone() const
    	{
    		std::unique_ptr<DummyDerive> tmp(new DummyDerive);
    		return std::move(tmp);
    	}
    };
    
    class Derive : public Base
    {
    	std::unique_ptr<Base> ptr;
    
    public:
    	Derive()
    		: ptr(new DummyDerive)
    	{ }
    
    	Derive(Derive const& d)
    	{
    		this->ptr = std::move(d.clone());
    	}
    
    	Derive& operator=(Derive const& d)
    	{
    		this->swap(d);
    		return *this;
    	}
    
    	void swap(Derive d)
    	{
    		std::swap(this->ptr, d.ptr);
    	}
    
    public:
    	virtual std::unique_ptr<Base> clone() const // Dummy Implementierung
    	{
    		std::unique_ptr<Base> null;
    		return null;
    	}
    };
    // Code ist unter Visual C++ 2012 fehlerfrei kompilierbar
    

    Wie ihr seht geht es hier um Fälle, wenn eine Klasse Smartpointer auf Klassenhierarchien hat. Gelöscht werden muss nichts, denn das erledigt der smarte Zeiger. Nur das Kopieren stellt sich mehr doer weniger als Problem dar (je nachdem was man wie hat, im aktuellen Projekt war es schon ein dicker Hund).

    Aber genau sowas widersprucht doch der Regel der Großen 3

    Oder liege ich irgendwo falsch? (Denn genauso fühlt es sich an, ohne eigenen Destruktor)



  • Die Regel ist mehr so eine Richtlinie, als ein Gesetz. Steht ja auch explizit drin, wo Du auch nachschaust.

    Außerdem ist sie wesentlich schärfer mit "Wenn ein Destruktor gebraucht wird, dann vermutlich auch die anderen beiden".

    Außerdem sollte man die anderen beiden viel öfter verbieten, als sich eine unintuitive Semantik auszudenken.



  • Base braucht einen virtuellen Destruktor, sonst ruft der Destruktor des Smart-Pointers immer den falschen auf.



  • Ok, und es zielt in Richtung Speicherverwaltung ab?

    Obiger Code ist damit vollkommen richtig? (und als Beispielcode wie gewohnt vollkommen sinnlos xD)



  • Immer das selbe Problem mit ad hoc reingetippten Beispielen:

    Derive& operator=(Derive const& d) 
         { 
             this->swap(d); 
             return *this; 
         }
    

    Das const& muss weg.

    EDIT:

    void swap(Derive d) 
         { 
             std::swap(this->ptr, d.ptr); 
         }
    

    Das compiliert und lässt sich auch aufrufen und alles, macht aber nicht das was man von einer swap() Funktion normalerweise erwarten würde...



  • Haha, genau umgekehrt, ja hast Recht.

    Der Kopf des Zuweisungsoperator muss eine Kopie erwarten und die swap Funktion eine non-const Referenz 😃


Anmelden zum Antworten