web-dev-qa-db-de.com

Was sind die Vorteile von boost :: noncopyable?

Um das Kopieren einer Klasse zu verhindern, können Sie ganz einfach einen privaten Kopierkonstruktor/Zuweisungsoperator deklarieren. Sie können aber auch boost::noncopyable.

Was sind die Vor-/Nachteile der Verwendung von Boost in diesem Fall?

63
tenfour

Zusammenfassend, was andere gesagt haben:

Vorteile von boost::noncopyable über private Kopiermethoden:

  1. Es ist in der Absicht expliziter und beschreibender. Die Verwendung privater Kopierfunktionen ist eine Redewendung, deren Erkennung länger dauert als die von noncopyable.
  2. Es ist weniger Code/weniger Tippen/weniger Unordnung/weniger Raum für Fehler (am einfachsten wäre es, versehentlich eine Implementierung bereitzustellen).
  3. Ähnlich wie bei einem C # -Attribut wird die Bedeutung direkt in die Metadaten des Typs eingebettet. Sie können jetzt eine Funktion schreiben, die nur Objekte akzeptiert, die nicht kopiert werden können.
  4. Möglicherweise werden Fehler früher im Erstellungsprozess abgefangen. Der Fehler wird eher zur Kompilierungszeit als zur Link-Zeit angezeigt, falls die Klasse selbst oder Freunde der Klasse das fehlerhafte Kopieren ausführen.
  5. (fast identisch mit # 4) Verhindert, dass die Klasse selbst oder Freunde der Klasse die privaten Kopiermethoden aufrufen.

Vorteile von privaten Kopiermethoden gegenüber boost::noncopyable:

  1. Keine Abhängigkeit vom Boost
41
tenfour

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 können nicht kopiert werden, 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:

3

Aber dies, von dem ich glaube, dass es überlegene Unterlagen gibt:

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:

2

Ich finde es viel einfacher, meine Kopiervorgänge zu deklarieren, als zu überlegen, ob ich von boost::non_copyable mehrmals und wenn mich das kosten wird. Vor allem, wenn ich nicht der Autor der gesamten Vererbungshierarchie bin.

46
Howard Hinnant

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 sich befindet - deklariert, um festzustellen, ob die Klasse nicht kopierbar ist oder nicht. Eine andere Möglichkeit, dies festzustellen, indem Sie Code schreiben, für den die Kopiersemantik aktiviert sein muss, und den Kompilierungsfehler anzeigen.

42
Nawaz
  1. Die Absicht von boost :: noncopyable ist klarer.
  2. Boost :: noncopyable verhindert, dass die Klassenmethoden versehentlich den Konstruktor für private Kopien verwenden.
  3. Weniger Code mit boost :: noncopyable.
16
thiton

Ich kann nicht verstehen, warum es sonst niemand zu erwähnen scheint, aber:

Mit noncopyable schreiben Sie nur einmal den Namen Ihrer Klasse.

Ohne, fünffache Vervielfältigung: Ein A für 'Klasse A', zwei zum Deaktivieren der Zuweisung und zwei zum Deaktivieren des Kopierkonstruktors.

16
ansgri

Zitieren der Dokumentation:

"Die traditionelle Art, mit diesen umzugehen, besteht darin, einen privaten Kopierkonstruktor und eine Kopierzuweisung zu deklarieren und dann zu dokumentieren, warum dies getan wird. Das Ableiten von nicht kopierbaren Dateien ist jedoch einfacher und klarer und erfordert keine zusätzliche Dokumentation. "

http://www.boost.org/libs/utility/utility.htm#Class_noncopyable

9
Viktor Sehr

Ein konkreter Vorteil (abgesehen davon, dass Sie Ihre Absicht etwas deutlicher zum Ausdruck bringen müssen) ist, dass der Fehler beim Kompilieren eher auftritt als beim Verknüpfen, wenn eine Member- oder Friend-Funktion versucht, ein Objekt zu kopieren. Auf den Basisklassenkonstruktor/die Basisklassenzuweisung kann nirgendwo zugegriffen werden, was zu einem Kompilierungsfehler führt.

Es verhindert auch, dass Sie versehentlich die Funktionen definieren (d. H. {} Anstatt von ;), ein kleiner Fehler, der möglicherweise unbemerkt bleibt, es Mitgliedern und Freunden jedoch ermöglicht, ungültige Kopien des Objekts anzufertigen.

8
Mike Seymour

Ein kleiner Nachteil (GCC-spezifisch) ist, dass Sie Ihr Programm mit g++ -Weffc++ Kompilieren und Klassen haben, die Zeiger enthalten, z.

class C : boost::noncopyable
{
public:
  C() : p(nullptr) {}

private:
  int *p;
};

GCC versteht nicht, was passiert:

warnung: 'Klasse C' hat Zeigerdatenmember [-Weffc ++]
Warnung: überschreibt aber nicht 'C (const S &)' [-Weffc ++]
Warnung: oder 'operator = (const C &)' [-Weffc ++]

Während es sich nicht beschwert 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 GCC-Effc ++ mehrere Probleme hat. Der Code, der nach "Problemen" sucht, ist sowieso ziemlich simpel ... manchmal hilft er.

3
manlio

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.

3
Nikko

Ich würde lieber boost :: noncopyable verwenden, als den Kopierkonstruktor und den Zuweisungsoperator manuell zu löschen oder zu privatisieren.

Jedoch verwende ich fast nie entweder Methode, weil:

Wenn ich ein nicht kopierbares Objekt erstelle, muss es einen Grund haben, warum es nicht kopierbar ist. Dieser Grund ist, 99% der Zeit, weil ich Mitglieder habe, die nicht sinnvoll kopiert werden können. Wahrscheinlich sind solche Mitglieder auch besser als private Implementierungsdetails geeignet. Also mache ich die meisten solcher Klassen wie folgt:

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 hieraus resultierenden Linkfehler sind verständlich, da sie beschreiben, 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 ist, dass ich später einfach einen Kopierkonstruktor und/oder einen Zuweisungsoperator hinzufügen und implementieren kann, ohne die Klassenhierarchie zu ändern, wenn ich mich dazu entscheide, meine Objekte dieser Klasse tatsächlich kopierbar zu machen.

2
wjl

Der Nachteil, laut Scott Meyers, ist der Name "nicht-natrual", wenn Sie einen Nachteil davon finden müssen.

1
H Xu