クラスのコピーを防ぐために、プライベートコピーコンストラクター/代入演算子を非常に簡単に宣言できます。ただし、boost::noncopyable
を継承することもできます。
この場合、ブーストを使用する利点/欠点は何ですか?
他の人が言ったことを要約する:
プライベートコピーメソッドに対するboost::noncopyable
の利点:
noncopyable
よりも見つけるのに時間がかかるイディオムです。boost::noncopyable
を介したプライベートコピーメソッドの利点:
ドキュメンテーションの利点はありません:
#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
から複数回派生しているかどうかを判断するよりも、コピー操作を宣言する方がはるかに簡単であり、それがコストのかかる場合です。特に、完全な継承階層の作成者ではない場合。
意図をexplicit and clearにします。それ以外の場合は、クラスの定義を確認し、copy-semanticに関連する宣言を検索し、それが含まれているaccess-specifierを探します。 宣言済み、クラスがコピー不可かどうかを判断するため。コピーセマンティックを有効にする必要があるコードを記述してコンパイルエラーを確認することにより、それを発見する他の方法。
他の誰もそれを言及していないのはなぜかわかりませんが、
noncopyable
を使用して、クラスの名前を1回だけ記述します。
なし、5つ折りの複製:「クラスA」に1つ、割り当てを無効にする2つ、コピーコンストラクターを無効にする2つ。
ドキュメントの引用:
」これらに対処する従来の方法は、プライベートコピーコンストラクターとコピーの割り当てを宣言し、これが行われた理由を文書化することです。追加のドキュメント。 "
http://www.boost.org/libs/utility/utility.htm#Class_noncopyable
具体的な利点(意図をもう少し明確に表現する以外に)は、メンバー関数またはフレンド関数がオブジェクトをコピーしようとすると、リンク段階ではなくコンパイル段階でエラーがより早くキャッチされることです。基本クラスのコンストラクター/割り当てはどこからでもアクセスできず、コンパイルエラーが発生します。
また、誤って関数を定義することを防ぎます(つまり、{}
の代わりに ;
)、気付かない可能性がありますが、メンバーや友人がオブジェクトの無効なコピーを作成できるようにする小さなエラー。
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 ++にはいくつかの問題があります。とにかく「問題」をチェックするコードはかなり単純です...時には役立つことがあります。
利点は、プライベートコピーコンストラクタとプライベートコピーオペレータを自分で記述する必要がなく、ドキュメントを追加せずに意図を明確に表現できることです。
コピーコンストラクタと代入演算子を手動で削除または民営化するよりも、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つに統合されています。
このパターンの利点は後であります。実際にこのクラスのオブジェクトをコピー可能にしたいと決めた場合、クラスの階層を変更せずにコピーコンストラクタや代入演算子を追加および実装することができます。
不利な点は、スコット・マイヤーズによると、不利な点を見つける必要がある場合、「非自然」です。