复制构造带的unique_ptr类(Copy constructor for a class wit

2019-08-31 07:24发布

如何实现对于具有的类的拷贝构造函数unique_ptr成员变量? 我只考虑C ++ 11。

Answer 1:

由于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提到,使用一招 - 构造函数,而不是一个拷贝构造函数,但是这会导致你的类的不同的语义。 一招-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 ,你基本上要决定是否矢量应为对象的唯一拥有者,在这种情况下,将足以使课堂活动,但不可拷贝。 如果你离开了拷贝构造函数和拷贝赋值,编译器会指导你如何使用只招类型一个std :: vector的方式。



Answer 2:

一个常见的情况有一个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可复制的。

为此,人们需要做包含指针的深拷贝 ,以确保派生类中正确地复制。

这可以通过添加以下代码来完成:

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

基本上有两件事情会在这里:

  • 首先是增加了复制和移动构造函数,这是在暗示删除的Foo作为的拷贝构造函数unique_ptr被删除。 此举构造可以简单地通过添加= default ...这只是让编译器知道,通常的移动构造函数不得删除(这工作,因为unique_ptr已经有一个移动构造函数可以在这种情况下使用)。

    对于拷贝构造函数Foo ,有没有类似的机制,有没有拷贝构造函数unique_ptr 。 因此,人们必须建立一个新unique_ptr ,与原来的指针对象的副本填充它,并把它作为复制的类的成员。

  • 在继承的情况下参与,原来指针对象的副本必须谨慎进行。 其原因是,操作的方式从一个简单的复制std::unique_ptr<Base>(*ptr)在上面的代码将导致切片,即,只有对象的基本成分被复制,派生部分缺失而。

    为了避免这种情况,复印,必须通过克隆模式来完成。 我们的想法是通过虚拟函数做复制clone_impl()返回一个Base*在基类。 在派生类中,但是,它是通过协方差扩大到返回Derived* ,而这个指针指向派生类的一个新创建的副本。 然后基类可以通过基类指针访问此新对象Base* ,它包裹成unique_ptr ,并且经由实际返回它clone()其从外部调用的函数。



Answer 3:

试试这个助手创建深层副本,当源的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;
};


Answer 4:

丹尼尔·弗雷提及关于复制的解决方案,我就说说如何移动的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

}

您需要通过STD包a和c ::举动,因为他们有一个名称的std ::此举告诉编译器来转换值右值引用任何参数都在技术意义上说,性病::此举是比喻像“的std ::右值”

移动后,的unique_ptr的资源转移到其他的unique_ptr

有迹象表明,记录右值引用许多议题; 这是一个非常简单的一个开始 。

编辑:

该移动的对象应继续有效,但不确定状态 。

C ++底漆5,也CH13提供有关如何“移动”的对象一个很好的解释



Answer 5:

我建议使用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;
}


Answer 6:

unique_ptr是不可拷贝,它只是移动。

这将直接影响到测试,这一点,在你的第二个,例如也只可移动和不可拷贝。

事实上,这是件好事,你使用unique_ptr可以保护你从一个很大的错误。

例如,你的第一个代码的主要问题是,指针永远不会删除这是真的,真的很糟糕。 再说了,你会被解决这个问题:

class Test
{
    int* ptr; // writing this in one line is meh, not sure if even standard C++

    Test() : ptr(new int(10)) {}
    ~Test() {delete ptr;}
};

int main()
{       
     Test o;
     Test t = o;
}

这也是不好的。 会发生什么,如果你复制Test ? 有将是一个指向同一个地址的指针两班。

当一个Test被破坏,还会破坏指针。 当你的第二个Test被破坏,它会尝试删除内存指针背后,也是如此。 但是它已经被删除,我们会得到一些坏的内存访问运行时错误(或不确定的行为,如果我们运气不好)。

所以,正确的做法是,要么实现拷贝构造函数和拷贝赋值运算符,这样的行为是明确的,我们可以创建一个副本。

unique_ptr是走在我们前面的位置。 它具有语义:“ 我是unique ,所以你不能只是复制了我。”于是,它妨碍我们现在手头实现运营商的错误。

您可以定义拷贝构造函数和拷贝赋值运算符的特殊行为和你的代码将工作。 但是你,这是理所当然的(!),被迫这样做。

这个故事的寓意是:始终使用unique_ptr在这些种情况。



文章来源: Copy constructor for a class with unique_ptr