Um das Kopieren einer Klasse zu verhindern, können Sie ganz einfach einen Konstruktor / Zuweisungsoperator für private Kopien deklarieren. Sie können aber auch erben boost::noncopyable
.
Was sind in diesem Fall die Vor- und Nachteile der Verwendung von Boost?
c++
boost
noncopyable
zehn vier
quelle
quelle
struct Foo{Foo(const Foo&)=delete;};
Foo & operator=(const Foo &) = delete;
?Antworten:
Ich sehe keinen Dokumentationsvorteil:
#include <boost/noncopyable.hpp> struct A : private boost::noncopyable { };
vs:
struct A { A(const A&) = delete; A& operator=(const A&) = delete; };
Wenn Sie Nur-Verschieben-Typen hinzufügen, sehe ich die Dokumentation sogar als irreführend an. Die folgenden zwei Beispiele sind nicht kopierbar, obwohl sie beweglich sind:
#include <boost/noncopyable.hpp> struct A : private boost::noncopyable { A(A&&) = default; A& operator=(A&&) = default; };
vs:
struct A { A(A&&) = default; A& operator=(A&&) = default; };
Bei Mehrfachvererbung kann es sogar zu einer Platzstrafe kommen:
#include <boost/noncopyable.hpp> struct A : private boost::noncopyable { }; struct B : public A { B(); B(const B&); B& operator=(const B&); }; struct C : public A { }; struct D : public B, public C, private boost::noncopyable { }; #include <iostream> int main() { std::cout << sizeof(D) << '\n'; }
Für mich druckt dies aus:
Aber das, von dem ich glaube, dass es eine überlegene Dokumentation hat:
struct A { A(const A&) = delete; A& operator=(const A&) = delete; }; struct B : public A { B(); B(const B&); B& operator=(const B&); }; struct C : public A { C(const C&) = delete; C& operator=(const C&) = delete; }; struct D : public B, public C { D(const D&) = delete; D& operator=(const D&) = delete; }; #include <iostream> int main() { std::cout << sizeof(D) << '\n'; }
Ausgänge:
Ich finde es viel einfacher, meine Kopiervorgänge zu deklarieren, als zu überlegen, ob ich
boost::non_copyable
mehrmals davon abgeleitet bin oder nicht und ob mich das kosten wird. Vor allem, wenn ich nicht der Autor der vollständigen Vererbungshierarchie bin.quelle
boost::noncopyable
war lange vor C ++ 11 verfügbar und kompilierte Unterstützung für= delete
. Ich stimme Ihnen zu, dass C ++ 11-kompatible Compiler mittlerweile veraltet sind.noncopyable
CRTP-Basisklasse so, dass alle Basisklassen in der Hierarchie eindeutig sind.private: __copy_constructor__;
es vollständig portabel ist und Sie keine ~ 40 MB Boost-Abhängigkeiten benötigen.std::vector<std::unique_ptr<animal>>
bevor ich nachboost::ptr_vector<animal>
( boost.org/doc/libs/1_54_0/libs/ptr_container/doc/tutorial.html ) greife . Begründung: Wenn ich weißvector
und ich weißunique_ptr
, dann kenne ich die Semantik von Vektoren von unique_ptr. Und ich weiß, wie die std :: -Algorithmen (zB sort) damit interagieren. Ich muss nicht alles über einen neuen Container mit seinen Mitgliedsalgorithmen lernen (z. B. Mitgliedersortierung).Es macht die Absicht explizit und klar , andernfalls muss man die Definition der Klasse sehen und nach der Deklaration suchen, die sich auf die Kopiersemantik bezieht, und dann nach dem Zugriffsspezifizierer suchen, in dem sie deklariert ist , um festzustellen, ob die Klasse ist nicht kopierbar oder nicht. Eine andere Möglichkeit, dies zu erkennen, indem Sie Code schreiben, für den eine kopiersemantische Aktivierung erforderlich ist, und den Kompilierungsfehler anzeigen.
quelle
noncopyable
. Es ist also ein strittiger Punkt.Zusammenfassend, was andere gesagt haben:
Vorteile
boost::noncopyable
gegenüber privaten Kopiermethoden :noncopyable
.Vorteile privater Kopiermethoden gegenüber
boost::noncopyable
:quelle
quelle
Ich kann nicht verstehen, warum es sonst niemand zu erwähnen scheint, aber:
Mit
noncopyable
Ihnen schreiben Sie den Namen Ihrer Klasse nur einmal.Ohne fünffache Vervielfältigung : Ein A für 'Klasse A', zwei zum Deaktivieren der Zuweisung und zwei zum Deaktivieren des Kopierkonstruktors.
quelle
Zitieren der Dokumentation:
"Der traditionelle Weg, um damit umzugehen, besteht darin, einen Konstruktor für private Kopien und eine Kopierzuweisung zu deklarieren und dann zu dokumentieren, warum dies getan wird. Die Ableitung von nicht kopierbar ist jedoch einfacher und klarer und erfordert keine zusätzliche Dokumentation."
http://www.boost.org/libs/utility/utility.htm#Class_noncopyable
quelle
Ein konkreter Vorteil (abgesehen davon, dass Sie Ihre Absicht etwas klarer ausdrücken) ist, dass der Fehler früher erkannt wird, in der Kompilierungsphase und nicht in der Verknüpfungsphase, wenn eine Mitglied- oder Freundfunktion versucht, ein Objekt zu kopieren. Auf den Konstruktor / die Zuweisung der Basisklasse kann nirgendwo zugegriffen werden, was zu einem Kompilierungsfehler führt.
Es verhindert auch die Funktionen versehentlich definieren (dh Typisierung
{}
statt;
), einen kleinen Fehler , die auch unbemerkt bleiben können, aber die würden dann Mitglieder und Freunde , damit ungültige Kopien des Objekts machen.quelle
...is that the error will be caught sooner, at the compile stage not the link stage
. Wie genau? Tut sogarboost::noncopyable
das Gleiche, was Sie tun würden, wenn Sie es nicht verwenden.noncopyable
Basisklasse verwenden, deklarieren Sie einen privaten Konstruktor in Ihrer Klasse. Der Zugriff ist für die Mitglieder und Freunde der Klasse zugänglich, sodass kein Kompilierungsfehler vorliegt - nur ein Linkfehler aufgrund der fehlenden Definition. (Sofern Sie nicht versehentlich eine Definition angeben - die Verwendung einer Basisklasse verhindert auch diesen Fehler).Der Vorteil ist, dass Sie keinen privaten Kopierkonstruktor und keinen privaten Kopieroperator selbst schreiben müssen und dies Ihre Absicht klar zum Ausdruck bringt, ohne zusätzliche Dokumentation zu schreiben.
quelle
Ein kleiner Nachteil (GCC-spezifisch) ist, dass, wenn Sie Ihr Programm mit kompilieren
g++ -Weffc++
und Klassen haben, die Zeiger enthalten, zclass C : boost::noncopyable { public: C() : p(nullptr) {} private: int *p; };
GCC versteht nicht, was passiert:
Während es sich nicht beschweren wird mit:
#define DISALLOW_COPY_AND_ASSIGN(Class) \ Class(const Class &) = delete; \ Class &operator=(const Class &) = delete class C { public: C() : p(nullptr) {} DISALLOW_COPY_AND_ASSIGN(C); private: int *p; };
PS Ich weiß, dass GCCs -Weffc ++ mehrere Probleme hat. Der Code, der nach "Problemen" sucht, ist sowieso ziemlich simpel ... manchmal hilft es.
quelle
Ich würde lieber boost :: noncopyable verwenden, als den Kopierkonstruktor und den Zuweisungsoperator manuell zu löschen oder zu privatisieren.
Ich benutze es jedoch fast nie entweder Methoden, weil:
Wenn ich ein nicht kopierbares Objekt erstelle, muss es einen Grund geben, warum es nicht kopierbar ist. Dieser Grund liegt in 99% der Fälle darin, dass ich Mitglieder habe, die nicht sinnvoll kopiert werden können. Möglicherweise eignen sich solche Mitglieder auch besser als Details zur privaten Implementierung. Also mache ich die meisten solcher Klassen so:
struct Whatever { Whatever(); ~Whatever(); private: struct Detail; std::unique_ptr<Detail> detail; };
Jetzt habe ich eine private Implementierungsstruktur und da ich std :: unique_ptr verwendet habe, kann meine Klasse der obersten Ebene nicht kostenlos kopiert werden. Die daraus resultierenden Linkfehler sind verständlich, da sie darüber sprechen, wie Sie ein std :: unique_ptr nicht kopieren können. Für mich sind dies alle Vorteile von boost :: noncopyable und einer privaten Implementierung in einem.
Der Vorteil dieses Musters besteht später darin, dass ich, wenn ich beschließe, meine Objekte dieser Klasse tatsächlich kopierbar zu machen, einfach einen Kopierkonstruktor und / oder einen Zuweisungsoperator hinzufügen und implementieren kann, ohne die Klassenhierarchie zu ändern.
quelle
Der Nachteil, laut Scott Meyers, ist der Name "nicht natürlich", wenn Sie einen Nachteil davon finden müssen.
quelle