absichtliches slicing bei swap-implementierung



  • hallo!

    class foo
    {
    	int a;
    public:
    	void swap(foo&x) { std::swap(a,x.a); }
    };
    class bar : public foo
    {
    	int b;
    public:
    	void swap(bar&x) { foo::swap(x); std::swap(b,x.b); }
    };
    

    ist in diesem code das absichtliche slicing in zeile 11 i.o.? slicing wird - so wie auch andere features aus C++ - immer schlecht dargestellt aber ich sehe gerade nicht, wieso es hier schlecht sein solle, geschweige denn, wie man das problem anders lösen könnte. jemand eine idee?

    LG



  • schlangenmensch420 schrieb:

    ...ist in diesem code das absichtliche slicing in zeile 11 i.o.? slicing wird - so wie auch andere features aus C++ - immer schlecht dargestellt aber ich sehe gerade nicht, wieso es hier schlecht sein solle, geschweige denn, wie man das problem anders lösen könnte.

    Na ja - rein optisch dreht es mir hier schon die Zehennägel hoch.

    Formal ist das zwar korrekt, aber die erste Frage ist doch, wozu dieses konkrete swap hier überhaupt gut sein soll. Einfach weglassen und im Falle eines Falles std::swap benutzen wäre IMHO eine gute Alternative.
    Der Code in bar::swap enthält zudem die implizite Annahme, dass bar von foo genauso abgeleitet ist. Bei einer Mini-Klasse mit drei Zeilen ist das kein Problem. Bei größeren Konstruktionen könnte man bei einer Änderung der Ableitungshierachie (z.B. Einfügen einer weiteren Basisklasse zwischen foo und bar ) hier vergessen, das swap anzupassen. Das kann dann zu übelsten Fehlern führen, die Du tagelang suchen darfst!

    Die Alternative ist ganz klar: weglassen! Und wenn bar oder foo komplizierter werden, so kümmere man sich zunächst um einen vernünftigen Move-Assignment-Operator. Dieser wird nämlich von std::swap aufgerufen.
    Und wenn tatsächlich beide Klassen so kompliziert werden, dass bei beiden Klassen ein individuelles swap angebracht zu sein scheint, dann ist meines Erachtens das Klassendesign zu hinterfragen.

    Btw.: den foo::swap mit public Zugriff dürfte es gar nicht geben. Denn das kann ja auch jemand anders rufen, mit einem foo* , der auf ein bar -Objekt zeigt. Das sollte mindestens protected sein!

    Gruß
    Werner



  • hallo werner!

    danke für deine antwort. dein argument, dass änderungen in der klassenhierarchie dann einfluss auf das verhalten dieser methode haben, ist sehr valide und mir fiele spontan leider auch kein statischer schutzmechanismus ein, denn z.b. std::is_base_of greift ja dennoch.

    bezüglich der alternative mit move-assign-operatoren: im echten projekt arbeite ich in einer hochgradig parallelen umgebung. beim moven muss ich mehrere mutexe (mutizen?) locken. wenn ich nun dreimal move, dann habe ich 3n locks. zudem kann ich dort die mutexe auch nicht effizient locken (wie es z.b. std::lock täte), sondern muss sequenziell vorgehen. mit meinem swap brauche ich nun lediglich 2n locks, die ich auch alle gleichzeitig machen kann (i.d.r. schneller).

    die anmerkung wegen der sichtbarkeit ist gold wert, das habe ich tatsächlich vergessen! danke!



  • Solltest du dann nicht ein swapNoLock() machen, damit du bei derived::swap nicht insgesamt 2x locken musst (1x in derived::swap selbst und 1x in base::swap).


Anmelden zum Antworten