web-dev-qa-db-ja.com

boost :: noncopyableの利点は何ですか

クラスのコピーを防ぐために、プライベートコピーコンストラクター/代入演算子を非常に簡単に宣言できます。ただし、boost::noncopyableを継承することもできます。

この場合、ブーストを使用する利点/欠点は何ですか?

63
tenfour

他の人が言ったことを要約する:

プライベートコピーメソッドに対するboost::noncopyableの利点

  1. 意図の中でより明示的で記述的です。プライベートコピー関数の使用は、noncopyableよりも見つけるのに時間がかかるイディオムです。
  2. コードが少なく、タイピングが少なく、混乱が少なく、エラーの余地が少ない(最も簡単なのは、誤って実装を提供することです)。
  3. C#属性と同様に、型のメタデータに意味を埋め込みます。コピー不可のオブジェクトのみを受け入れる関数を作成できるようになりました。
  4. ビルドプロセスの早い段階でエラーをキャッチする可能性があります。クラス自体またはクラスの友人が誤ったコピーを行っている場合、エラーはリンク時ではなくコンパイル時に表示されます。
  5. (ほぼ#4と同じ)クラス自体またはクラスのフレンドがプライベートコピーメソッドを呼び出さないようにします。

boost::noncopyableを介したプライベートコピーメソッドの利点

  1. ブースト依存なし
41
tenfour

ドキュメンテーションの利点はありません:

#include <boost/noncopyable.hpp>

struct A
    : private boost::noncopyable
{
};

対:

struct A
{
     A(const A&) = delete;
     A& operator=(const A&) = delete;
};

移動専用タイプを追加すると、ドキュメンテーションが誤解を招くものであるとさえ感じます。次の2つの例はコピーできませんが、移動可能です。

#include <boost/noncopyable.hpp>

struct A
    : private boost::noncopyable
{
    A(A&&) = default;
    A& operator=(A&&) = default;
};

対:

struct A
{
    A(A&&) = default;
    A& operator=(A&&) = default;
};

多重継承の下では、スペースのペナルティもあります。

#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';
}

私にとってこれは印刷されます:

3

しかし、これは、優れたドキュメントがあると信じています:

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';
}

出力:

2

boost::non_copyableから複数回派生しているかどうかを判断するよりも、コピー操作を宣言する方がはるかに簡単であり、それがコストのかかる場合です。特に、完全な継承階層の作成者ではない場合。

46
Howard Hinnant

意図をexplicit and clearにします。それ以外の場合は、クラスの定義を確認し、copy-semanticに関連する宣言を検索し、それが含まれているaccess-specifierを探します。 宣言済み、クラスがコピー不可かどうかを判断するため。コピーセマンティックを有効にする必要があるコードを記述してコンパイルエラーを確認することにより、それを発見する他の方法。

42
Nawaz
  1. Boost :: noncopyableの意図はより明確です。
  2. Boost :: noncopyableは、クラスメソッドが誤ってプライベートコピーコンストラクターを使用するのを防ぎます。
  3. Boost :: noncopyableでコードが少なくなります。
16
thiton

他の誰もそれを言及していないのはなぜかわかりませんが、

noncopyableを使用して、クラスの名前を1回だけ記述します。

なし、5つ折りの複製:「クラスA」に1つ、割り当てを無効にする2つ、コピーコンストラクターを無効にする2つ。

16
ansgri

ドキュメントの引用:

」これらに対処する従来の方法は、プライベートコピーコンストラクターとコピーの割り当てを宣言し、これが行われた理由を文書化することです。追加のドキュメント。 "

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

9
Viktor Sehr

具体的な利点(意図をもう少し明確に表現する以外に)は、メンバー関数またはフレンド関数がオブジェクトをコピーしようとすると、リンク段階ではなくコンパイル段階でエラーがより早くキャッ​​チされることです。基本クラスのコンストラクター/割り当てはどこからでもアクセスできず、コンパイルエラーが発生します。

また、誤って関数を定義することを防ぎます(つまり、{} の代わりに ;)、気付かない可能性がありますが、メンバーや友人がオブジェクトの無効なコピーを作成できるようにする小さなエラー。

8
Mike Seymour

small欠点(GCC固有)は、g++ -Weffc++を使用してプログラムをコンパイルし、ポインターを含むクラスがある場合、.

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

private:
  int *p;
};

GCCは何が起こっているのか理解していません。

警告:「クラスC」にはポインターデータメンバーがあります[-Weffc ++]
警告:ただし、 'C(const S)'をオーバーライドしません[-Weffc ++]
警告:または 'operator =(const C)' [-Weffc ++]

文句は言いませんが:

#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 GCCの-Weffc ++にはいくつかの問題があります。とにかく「問題」をチェックするコードはかなり単純です...時には役立つことがあります。

3
manlio

利点は、プライベートコピーコンストラクタとプライベートコピーオペレータを自分で記述する必要がなく、ドキュメントを追加せずに意図を明確に表現できることです。

3
Nikko

コピーコンストラクタと代入演算子を手動で削除または民営化するよりも、boost :: noncopyableを使用したいです。

ただし、eitherメソッドはほとんど使用しません。

コピー不可のオブジェクトを作成する場合、コピー不可の理由が必要です。この理由は、99%の確率で、意味のあるコピーができないメンバーがいるためです。そのようなメンバーは、プライベート実装の詳細としても適している可能性があります。そのため、このようなクラスのほとんどを次のように作成します。

struct Whatever {
  Whatever();
  ~Whatever();
  private:
  struct Detail;
  std::unique_ptr<Detail> detail;
};

これで、プライベート実装構造体ができました。std:: unique_ptrを使用したので、トップレベルのクラスは無料でコピーできません。これに起因するリンクエラーは、std :: unique_ptrをコピーできない方法について説明しているため、理解できます。私にとって、これはboost :: noncopyableのすべての利点であり、プライベート実装が1つに統合されています。

このパターンの利点は後であります。実際にこのクラスのオブジェクトをコピー可能にしたいと決めた場合、クラスの階層を変更せずにコピーコンストラクタや代入演算子を追加および実装することができます。

2
wjl

不利な点は、スコット・マイヤーズによると、不利な点を見つける必要がある場合、「非自然」です。

1
H Xu