web-dev-qa-db-ja.com

unique_ptrを持つクラスのコンストラクターをコピーします

unique_ptrメンバー変数を持つクラスのコピーコンストラクターを実装するにはどうすればよいですか? C++ 11のみを検討しています。

88
codefx

unique_ptrは共有できないため、コンテンツをディープコピーするか、unique_ptrshared_ptrに変換する必要があります。

class A
{
   std::unique_ptr< int > up_;

public:
   A( int i ) : up_( new int( i ) ) {}
   A( const A& a ) : up_( new int( *a.up_ ) ) {}
};

int main()
{
   A a( 42 );
   A b = a;
}

NPEが述べたように、コピーアクターの代わりにムーブアクターを使用できますが、クラスの異なるセマンティクスになります。 move-ctorは、std::moveを介してメンバーを明示的に移動可能にする必要があります。

A( A&& a ) : up_( std::move( a.up_ ) ) {}

必要な演算子の完全なセットを持つこともにつながります

A& operator=( const A& a )
{
   up_.reset( new int( *a.up_ ) );
   return *this,
}

A& operator=( A&& a )
{
   up_ = std::move( a.up_ );
   return *this,
}

クラスをstd::vectorで使用する場合は、ベクターがオブジェクトの一意の所有者であるかどうかを基本的に決定する必要があります。その場合、クラスを移動可能にできますが、コピーできません。 copy-ctorとcopy-assignmentを省略した場合、コンパイラーは、移動専用タイプでstd :: vectorを使用する方法をガイドします。

68
Daniel Frey

クラスにunique_ptrが含まれる通常のケースは、継承を使用できるようにすることです(そうでない場合は、プレーンオブジェクトでもよく行われます。RAIIを参照)。この場合、これまでこのスレッドには適切な答えがありません

つまり、ここが出発点です

struct Base
{
    //some stuff
};

struct Derived : public Base
{
    //some stuff
};

struct Foo
{
    std::unique_ptr<Base> ptr;  //points to Derived or some other derived class
};

...そして、目標は、前述のように、Fooをコピー可能にすることです。

このためには、派生クラスが正しくコピーされるように、含まれるポインターのdeep copyを行う必要があります。

これは次のコードを追加することで実現できます:

struct Base
{
    //some stuff

    auto clone() const { return std::unique_ptr<Base>(clone_impl()); }
protected:
    virtual Base* clone_impl() const = 0;
};

struct Derived : public Base
{
    //some stuff

protected:
    virtual Derived* clone_impl() const override { return new Derived(*this); };                                                 
};

struct Foo
{
    std::unique_ptr<Base> ptr;  //points to Derived or some other derived class

    //rule of five
    ~Foo() = default;
    Foo(Foo const& other) : ptr(other.ptr->clone()) {}
    Foo(Foo && other) = default;
    Foo& operator=(Foo const& other) { ptr = other.ptr->clone(); return *this; }
    Foo& operator=(Foo && other) = default;
};

ここでは、基本的に2つのことが行われています。

  • 1つ目は、コピーコンストラクターと移動コンストラクターの追加です。これらは、unique_ptrのコピーコンストラクターが削除されると、Fooで暗黙的に削除されます。移動コンストラクターは、単に= default ...で追加できます。これは、通常の移動コンストラクターがnotであることをコンパイラーに知らせるだけです。削除(これは機能します。unique_ptrには既にこの場合に使用できる移動コンストラクターがあるためです)。

    Fooのコピーコンストラクターには、unique_ptrのコピーコンストラクターがないため、同様のメカニズムはありません。そのため、新しいunique_ptrを作成し、元の指示先のコピーでそれを埋め、コピーされたクラスのメンバーとして使用する必要があります。

  • 継承が関係する場合は、元の指示先のコピーを慎重に行う必要があります。その理由は、上記のコードでstd::unique_ptr<Base>(*ptr)を介して単純なコピーを行うと、スライスが発生するためです。つまり、派生部分はなく、オブジェクトのベースコンポーネントのみがコピーされます。

    これを回避するには、複製パターンを介してコピーを行う必要があります。考え方は、基本クラスでBase*を返す仮想関数clone_impl()を介してコピーを行うことです。ただし、派生クラスでは、Derived*を返すように共分散を介して拡張され、このポインターは派生クラスの新しく作成されたコピーを指します。基本クラスは、基本クラスポインターBase*を介してこの新しいオブジェクトにアクセスし、unique_ptrにラップし、外部から呼び出される実際のclone()関数を介して返すことができます。

26
davidhigh

このヘルパーを使用してディープコピーを作成し、ソースunique_ptrがnullの場合に対処してください。

    template< class T >
    std::unique_ptr<T> copy_unique(const std::unique_ptr<T>& source)
    {
        return source ? std::make_unique<T>(*source) : nullptr;
    }

例えば:

class My
{
    My( const My& rhs )
        : member( copy_unique(rhs.member) )
    {
    }

    // ... other methods

private:
    std::unique_ptr<SomeType> member;
};
10
Scott Langham

ダニエル・フレイはコピーソリューションについて言及しましたが、unique_ptrを移動する方法について話します

#include <memory>
class A
{
  public:
    A() : a_(new int(33)) {}

    A(A &&data) : a_(std::move(data.a_))
    {
    }

    A& operator=(A &&data)
    {
      a_ = std::move(data.a_);
      return *this;
    }

  private:
    std::unique_ptr<int> a_;
};

これらは、ムーブコンストラクターおよびムーブ割り当てと呼ばれます

このように使用できます

int main()
{
  A a;
  A b(std::move(a)); //this will call move constructor, transfer the resource of a to b

  A c;
  a = std::move(c); //this will call move assignment, transfer the resource of c to a

}

Aとcをstd :: moveでラップする必要があるstd :: rvalue "

移動後、unique_ptrのリソースは別のunique_ptrに転送されます

右辺値参照を文書化する多くのトピックがあります。 これは非常に簡単なことです

編集:

移動されたオブジェクト 有効であるが指定されていない状態のまま

C++入門5、ch13もオブジェクトを「移動」する方法について非常に良い説明を与えます

5
StereoMatching

Make_uniqueを使用することをお勧めします

class A
{
   std::unique_ptr< int > up_;

public:
   A( int i ) : up_(std::make_unique<int>(i)) {}
   A( const A& a ) : up_(std::make_unique<int>(*a.up_)) {};

int main()
{
   A a( 42 );
   A b = a;
}
1
Splash