Klassendesign: Verbietet ihr alles was ihr nicht braucht?
-
Hallo,
man kann ja das Kopieren, Zuweisen und auch sämtliche Operatoren einer Klasse verbieten, indem man die entsprechenden Methoden private/explicit macht. Die wenigsten Klassen, die ich so geschrieben habe, sollen kopiert, zugewiesen oder sonst wie mit Operatoren behandelt werden.
Wenn ihr solche Klassen habt, verbietet ihr dann wirklich jeden Operator extra, weil das sind ja schon eine ganze Menge? Oder wie soll sonst der Anwender mitbekommen, dass er Objekte der Klassen beispielsweise nicht inkrementieren kann/soll?
Ich hoffe es versteht einer was ich meine^^
-
um ehrlich zu sein verstehe ich nicht, was du meinst. bis auf die großen drei(op=, copy ctor default ctor) erzeugt der Compiler keine weiteren Methoden. Wenn du auf einer beliebigen deiner Klassen op++ aufrufst, kriegst du einen Compilerfehler.
-
Du hast explicit nicht verstanden, das hat damit so erstmal nichts zu tun.
Zu deiner Frage: Ja, ich mache alles private was logisch private sein sollte. Das hat weniger was damit zu tun ob ich es außerhalb der Klasse brauche oder nicht. Entweder es gibt eine logische Möglichkeit ein Objekt der Klasse zu kopieren, oder eben nicht. Aber so viel Aufwand ist das nun nicht:class foo { // ... private: foo(const foo&); foo& operator = (const foo&); };Fertig.

-
Verhindere ich mit explicit vor einem Konstruktor nicht dass ich ihm dann nichts zuweisen kann?
Ich hatte gedacht dass der Compiler auch automatisch die Operatoren anlegt, wie beim Kon-/Destruktor etc., dass war mein Fehler.
-
Mit explicit verhinderst du nur implizite Konvertierung.
Direkte Zuweisung (=ohne implizite Konvertierung) bzw. das Erzeugen von Kopien verhinderst du dadurch nicht.Und ja, natürlich mache ich alle Klassen non-copyable und non-assignable wenn der implizit definierte Copy-Ctor oder Assignment-Operator Mist bauen würde.
Ausgenommen natürlich die Klassen die kopierbar oder zuweisbar sein sollen/müssen, in dem Fall definiere ich dann eben selbst einen passenden Copy-Konstruktor/Assignment-Operator.ps:
Wenn man nicht in jeder "noncopyable" Klasse den Copy-Konstruktor und Assignent-Operator private machen will bietet sich boost::noncopyable an.
Finde ich auch übersichtlicher/leserlicherclass Foo : private boost::noncopyable { ... };Bzw. wenn man Boost nicht verwenden will/kann ist so eine "noncopyable" Klasse auch in ein paar Sekunden selbst geschrieben.
-
Unter C++11 gibt es auch die Möglichkeit mit
= delete;was ich persönlich schöner finde. Die großen 5 (C++11) deklariere ich immer. Wenn ich sie nicht selbst implementiere, deklariere ich sie mit
= default;.
-
Ich kenne nur die Rulez-of-three, es gibt auch the-big-five? Hast du einen speziellen Compiler, weil es werden nur ctor, dtor, copy, assignment von alleine angelegt und wenn man einen von den dreien(dtor,copy,assign) anlegt, sollte man auch die anderen beiden implementieren. Warum steht z.B. in effective C++.
-
_sone_dreck schrieb:
es werden nur ctor, dtor, copy, assignment von alleine angelegt
In C++11 kommen noch move-Ctor und move-Assignment dazu...
-
pumuckl schrieb:
_sone_dreck schrieb:
es werden nur ctor, dtor, copy, assignment von alleine angelegt
In C++11 kommen noch move-Ctor und move-Assignment dazu...
Noch nicht ganz wach?
Die werden so nicht automatisch generiert.
-
cooky451 schrieb:
pumuckl schrieb:
_sone_dreck schrieb:
es werden nur ctor, dtor, copy, assignment von alleine angelegt
In C++11 kommen noch move-Ctor und move-Assignment dazu...
Noch nicht ganz wach?
Die werden so nicht automatisch generiert.Ich liefer gleich den Standard:
N3337 §12.8 Klausel 9 schrieb:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared
as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator,
— X does not have a user-declared destructor, and
— the move constructor would not be implicitly defined as deleted.N3337 §12.8 Klausel 20 schrieb:
If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly
declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared move constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared destructor, and
— the move assignment operator would not be implicitly defined as deleted.
-
Genau was ich sagte..
-
N3337 §12.8 Klausel 9 schrieb:
— the move constructor would not be implicitly defined as deleted.
Wann gilt der Punkt denn?
-
Cyres schrieb:
Die großen 5 (C++11) deklariere ich immer.
Lustig, ich fast nie.
Zumindest was die Grossen Drei angeht, sehe ich es von Vorteil, wenn die Member bereits Wertsemantik besitzen und automatisch kopiert werden. Das spart viel Boilerplatecode und auch mögliche Fehler, weil die Kopie eines Members vergessen wird. Bei Klassen, die Exceptions werfen, muss man allerdings etwas aufpassen...
-
Nein, implementiert werden sie nicht implizit, allerdings ist es von Vorteil, falls sie vorhanden sind.
Nexus schrieb:
Cyres schrieb:
Die großen 5 (C++11) deklariere ich immer.
Lustig, ich fast nie.
Ich persönlich finde es übersichtlicher, und auf die zwei Zeilen Code kommt es dann echt nicht mehr an. Rein Interesse halber, benutzt (falls du die Wahl hast) du "#pragma once" oder lieber Header-Guards?
-
Wann immer möglich
#pragma once. In einer plattformunabhängigen Bibliothek allerdings Header-Guards.Hm, fast alle aktuellen Compiler scheinen
#pragma oncezu unterstützen. Kennt ihr Beispiele, wo es nicht funktioniert? Sonst überlege ich mir, das auch in portablem Code zu verwenden.
-
Nexus schrieb:
Wann immer möglich
#pragma once. In einer plattformunabhängigen Bibliothek allerdings Header-Guards.Hm, fast alle aktuellen Compiler scheinen
#pragma oncezu unterstützen. Kennt ihr Beispiele, wo es nicht funktioniert? Sonst überlege ich mir, das auch in portablem Code zu verwenden.Mir sind keine Probleme bekannt, als ich rausgefunden habe, dass auch der GCC (und Clang)
#pragma onceunterstützen, hatte ich auch viel geändert, ich frage deshalb, weil da wieder die beiden Zeilen wären, die man sich bei#pragma oncesparen würde

-
cooky451 schrieb:
Genau was ich sagte..
Nope. Du sagtest "die werden nicht automatisch generiert". Werden sie aber doch, wenn alle anderen auch automatisch generiert werden.
Eisflamme schrieb:
N3337 §12.8 Klausel 9 schrieb:
— the move constructor would not be implicitly defined as deleted.
Wann gilt der Punkt denn?
Ich überlasse das Flöhen des Standards an der Stelle mal anderen, aber da der generierte Move-Ctor einfach alle Member moved, würde ich mal annehmen, dass das dann der Fall ist, wenn einer der Member nicht movable ist.
-
Nexus schrieb:
Kennt ihr Beispiele, wo es nicht funktioniert?
IBM XL C++ for AIX

Meines Wissens erkenne viele Präprozessoren include-Guards aber als das was sie sind und haben eine spezielle Merkvorrichtung dafür, d.h. #pragma once ist dann auch nicht schneller.
-
nahezu alle Borland bzw. CodeGear Compiler erkennen es auch nicht.
-
Nexus schrieb:
Cyres schrieb:
Die großen 5 (C++11) deklariere ich immer.
Lustig, ich fast nie.
Zumindest was die Grossen Drei angeht, sehe ich es von Vorteil, wenn die Member bereits Wertsemantik besitzen und automatisch kopiert werden. Das spart viel Boilerplatecode und auch mögliche Fehler, weil die Kopie eines Members vergessen wird. Bei Klassen, die Exceptions werfen, muss man allerdings etwas aufpassen...
Kannst du bitte das etwas genauer erklären, warum es bei Klassen die Execptions werfen schwieriger ist die rulez-of-three zu implementieren?
-
_sone_dreck schrieb:
Kannst du bitte das etwas genauer erklären, warum es bei Klassen die Execptions werfen schwieriger ist die rulez-of-three zu implementieren?
Es geht vor allem darum, was passiert wenn die entsprechenden Operationen der Member Exceptions werfen. Beispiel:
class AssignmentThrows; //Name sagt alles... class Problematic { int* p1; AssignmentThrows at; int* p2; //INVARIANTE: p2 > p1 };So, was passiert, wenn ich ein "Problematic" einem anderen zuweise?
p1 wird kopiert. OK. at wird kop...BUMM
Übrig bleibt ein "Problematic", bei dem p1 geändert wurde, p2 aber nicht. Die Invariante wird vermutlich nicht mehr gelten, und alles was sich darauf verlässt (z.B. der Destruktor) spielt Russisch Roulette. Kurz: der default-generierte op= ist nicht exceptoinsicher.
Das ist jetzt ein sehr merkwürdig konstruiertes Beispiel, aber es zeigt die Problematik, und die gibts in allen Formen, Farben und Geschmacksrichtungen.