如何为具有unique_ptr
成员变量的类实现复制构造函数?我只考虑C ++ 11。
std::vector
。
unique_ptr
如果您的目标是将数据放入a中,则可能不希望包含a的类的副本构造函数,而可能需要move构造函数std::vector
。另一方面,C ++ 11标准已自动创建了move构造函数,因此也许您确实想要一个复制构造函数……
如何为具有unique_ptr
成员变量的类实现复制构造函数?我只考虑C ++ 11。
std::vector
。
unique_ptr
如果您的目标是将数据放入a中,则可能不希望包含a的类的副本构造函数,而可能需要move构造函数std::vector
。另一方面,C ++ 11标准已自动创建了move构造函数,因此也许您确实想要一个复制构造函数……
Answers:
由于unique_ptr
无法共享,您需要深度复制其内容或将转换unique_ptr
为shared_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而不是copy-ctor,但这会导致类的语义不同。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与仅移动类型一起使用。
int
。如果您有一个unique_ptr<Base>
存储的Derived
,则上面的内容将切片。
A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}
value_ptr
- unique_ptr
加上删除器/复印机信息的设计。
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
};
...的目标是,如所说的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;
};
基本上有两件事正在发生:
第一个是添加了copy和move构造函数,Foo
随着的copy构造函数unique_ptr
被删除,它们会隐式删除。此举构造可以简单地通过添加= default
...这只是为了让编译器知道,通常的移动构造函数将不会被删除(此作品,unique_ptr
已经有一个移动构造函数可以在这种情况下使用)。
对于的复制构造函数Foo
,没有类似的机制unique_ptr
。因此,必须构造一个new unique_ptr
,用原始pointe的副本填充它,并将其用作复制的类的成员。
如果涉及继承,则必须仔细完成原始指针的副本。原因是std::unique_ptr<Base>(*ptr)
在上面的代码中进行简单复制会导致切片,即仅复制对象的基本组件,而缺少派生部分。
为了避免这种情况,必须通过克隆模式进行复制。这个想法是通过在基类中clone_impl()
返回a 的虚函数进行复制的Base*
。但是,在派生类中,它通过协方差扩展以返回a Derived*
,并且此指针指向派生类的新创建的副本。然后,基类可以通过基类指针访问此新对象Base*
,将其包装到中unique_ptr
,然后通过clone()
从外部调用的实际函数将其返回。
unique_ptr
直接包含的对象所指向的对象。答案???继承。
unique_ptr
在optional
对可空类型。
clone_impl
in base,编译器不会告诉您是否在派生类中忘记了它。但是,您可以使用另一个基类Cloneable
并在clone_impl
那里实现纯虚拟。然后,如果您在派生类中忘记它,编译器将抱怨。
尝试使用此帮助程序创建深层副本,并在源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;
};
丹尼尔·弗雷(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_;
};
它们被称为移动构造函数和移动分配
你可以这样使用它们
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 :: move包装a和c,因为它们的名称为std :: move告诉编译器将参数转换为右值引用,无论参数是什么。 std :: rvalue“
移动后,unique_ptr的资源将转移到另一个unique_ptr
有很多主题记录右值引用;这是一个很简单的开始。
编辑:
移动的对象应保持有效但未指定状态。
C ++入门5,ch13还对如何“移动”对象提供了很好的解释
a
在b
move构造函数中调用std :: move(a)之后对象会发生什么?只是完全无效吗?
unique_ptr
不可复制,只能移动。
这将直接影响Test,在您的第二个示例中,Test也只能移动而不能复制。
实际上,最好使用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
在这种情况下使用。