Dan*_*rey 70
由于unique_ptr
无法共享,您需要深层复制其内容或将其转换unique_ptr
为ashared_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;
}
Run Code Online (Sandbox Code Playgroud)
正如NPE所提到的,你可以使用move-ctor而不是copy-ctor,但这会导致你的类的语义不同.移动控制器需要使该成员明确地通过移动std::move
以下方式:
A( A&& a ) : up_( std::move( a.up_ ) ) {}
Run Code Online (Sandbox Code Playgroud)
拥有一套完整的必要操作员也会导致
A& operator=( const A& a )
{
up_.reset( new int( *a.up_ ) );
return *this,
}
A& operator=( A&& a )
{
up_ = std::move( a.up_ );
return *this,
}
Run Code Online (Sandbox Code Playgroud)
如果你想在a中使用你的类std::vector
,你基本上必须决定向量是否应该是对象的唯一所有者,在这种情况下,使类可移动,但不可复制就足够了.如果省略copy-ctor和copy-assignment,编译器将指导你如何使用只移动类型的std :: vector.
dav*_*igh 29
unique_ptr
在类中拥有a的通常情况是能够使用继承(否则普通对象通常也会这样做,请参阅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
};
Run Code Online (Sandbox Code Playgroud)
...... 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;
};
Run Code Online (Sandbox Code Playgroud)
这里基本上有两件事:
第一个是添加复制和移动构造函数,在删除Foo
复制构造函数时隐式删除它们unique_ptr
.移动构造函数可以简单地添加= default
...这只是为了让编译器知道通常的移动构造函数不会被删除(这是有效的,因为unique_ptr
已经有一个可以在这种情况下使用的移动构造函数).
对于复制构造函数Foo
,没有类似的机制,因为没有复制构造函数unique_ptr
.因此,必须构造一个new unique_ptr
,用原始指针的副本填充它,并将其用作复制类的成员.
如果涉及继承,则必须仔细完成原始指针的副本.原因是std::unique_ptr<Base>(*ptr)
在上面的代码中执行简单的复制会导致切片,即只复制对象的基本组件,而缺少派生的部分.
为避免这种情况,必须通过克隆模式完成复制.我们的想法是通过clone_impl()
一个Base*
在基类中返回a 的虚函数来复制.但是,在派生类中,它通过协方差扩展为返回a Derived*
,并且此指针指向新创建的派生类副本.然后,基类可以通过基类指针访问这个新对象Base*
,将其包装成a unique_ptr
,然后通过clone()
从外部调用的实际函数返回它.
Sco*_*ham 10
尝试使用此帮助程序创建深层副本,并在源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;
}
Run Code Online (Sandbox Code Playgroud)
例如:
class My
{
My( const My& rhs )
: member( copy_unique(rhs.member) )
{
}
// ... other methods
private:
std::unique_ptr<SomeType> member;
};
Run Code Online (Sandbox Code Playgroud)
Daniel Frey提到了复制解决方案,我会谈谈如何移动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_;
};
Run Code Online (Sandbox Code Playgroud)
它们被称为移动构造函数和移动赋值
你可以像这样使用它们
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
}
Run Code Online (Sandbox Code Playgroud)
你需要用std :: move包装a和c,因为它们有一个名字std :: move告诉编译器将值转换为rvalue引用,无论参数是什么,在技术意义上,std :: move类似于"的std ::右值"
移动后,unique_ptr的资源将转移到另一个unique_ptr
记录右值参考的主题很多; 这是一个非常简单的开始.
编辑:
移动的对象应保持有效但未指定的状态.
C++入门5,ch13也给出了关于如何"移动"对象的非常好的解释
我建议使用 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;
}
Run Code Online (Sandbox Code Playgroud)