复制构造函数用于转移 unique_ptr 的所有权

6

我需要编写一个复制构造函数,它还可以传递被复制对象的唯一指针成员的所有权。情况如下:

class C{
   // C class stuff       
};

class A{
public:
    public A();
    public A(const A& a);
private:
    std::unique_ptr<C> c_;
}

class B{

public:

    B(const A& b) : a_(a){}

private:
     A a_;


};

我应该如何为类 A 实现拷贝构造函数?

11
所以,当你制作一份副本时,你想使原始版本无效吗?我建议删除复制构造函数,仅使该类可移动。 - NathanOliver
如果您的原始对象是A,则应将“transferref”作为B类的成员。您能否提供一个例子? - gcswoosh
3
请阅读[tag:move-semantics]标签下的问题和答案。 - user3920237
3个回答

6

我猜你的意图或方法是错误的。

拷贝构造函数是用来创建参数的拷贝,但由于unique_ptr维护了唯一所有权,所以无法对其进行复制。实际上,你可以使unique_ptr成员可变,然后在拷贝构造函数中移动它指向的资源,但这是完全疯狂的做法(这就是std::auto_ptr的做法,也是为什么它被弃用的原因)。

因此,你需要:

  • 添加一个移动构造函数到A和B中(+使拷贝构造函数被删除)
  • 如果C确实需要被共享,那么将unique_ptr替换为shared_ptr

还有第三个选择,就是在A的拷贝构造函数中复制unique_ptr所指向的对象

A::A(const A& a) : c_(std::unique_ptr<C>(a.c_ ? new C(*a.c_) : nullptr)) {
}

我不会说意图是错误的。创建一个接口来建模常规类型,这对于包装类型无法自然地暴露是完全可以的。 - mavam
我不同意你的观点。你可以复制一个持有unique_ptr的对象。例如,我可能会实现一个LinkedList类,它将unique_ptr作为下一个元素的指针。这是否意味着我不能或不应该复制一个链表?我可能想要复制链表本身,并将该副本保存在复制对象的unique_ptr中。 - David Haim
4
@DavidHaim,但是原帖的标题指的是在拷贝构造函数中转移所有权。我认为这种方法是错误的。 - Adam Romanek
@DavidHaim实际上Adam Romanek是正确的,因为我的意图是转移所有权,而不是复制unique_ptr指向的对象。我可以问一个此示例的移动构造函数的例子吗? - gcswoosh
2
@Gabrielecswoosh,这是你要的链接:http://melpon.org/wandbox/permlink/bbvgsx31iuofzF4s。请注意,当您拥有一个仅可移动类型作为成员(例如unique_ptr)时,默认情况下编译器会为您生成相同的代码:http://melpon.org/wandbox/permlink/7AlOoXZSOMOSLiFa(请注意A中没有复制/移动构造函数,但仍按预期工作)。 - Adam Romanek
谢谢,实际上我在A中有其他成员变量,它们不是移动类型(即float和int),所以我需要编写它! - gcswoosh

4
显然,你不能像对待普通指针一样对待std::unique_ptr进行赋值操作,因为它们的赋值运算符被删除了。这是有意为之的,目的是迫使程序员定义所需操作的行为。
  1. 新项接管c_的所有权,使原始项无效。
  2. 新项复制c_,保留原始项的有效性。
  3. 新项共享c_的所有权,使新项和原始项都引用同一个对象。

情况1中,你需要的是移动构造函数,而默认的移动构造函数可以很好地工作。因此,你不需要编写任何代码,只需执行以下操作即可:
A temp;
A foo(std::move(temp));

请注意,temp在移动后将无效。

情况2中,您需要添加自定义的复制构造函数来创建原始c_的副本:

A(const A& a):c_(new C(*(a.c_))){}

在定义了这个变量之后,您可以这样操作:A
A foo(A());

请注意,这取决于C的复制构造函数是否可用。

第三种情况中,您需要从使用std::unique_ptr更改为使用std::shared_ptr,因此c_的定义将变为:

std::shared_ptr<C> c_;

您构建c_的方法与您已经在std::unique_ptr版本的c_中使用的方法完全相同。因此,只需使用默认实现即可:

A foo;
A bar(foo);

现在foobar指向同一个C对象,并共享对它的所有权。只有当所有引用该对象的shared_ptr都被删除后,才会删除这个共享对象。


3

从技术上讲,要编写一个复制构造函数,同时将被复制对象的unique_ptr成员的所有权转移,只需执行以下操作:

编写一个复制构造函数,同时在其中使用move操作符来转移unique_ptr成员的所有权。

你可以这样做:

class Bad
{
private:
    unique_ptr<int> p_;
public:
    Bad(): p_( new int(666) ) {}
    Bad( Bad& other )
       : p_( move( other.p_ ) )
    {}
};

一个复制构造函数可以有这个签名,再加上两个,除了更常规的 Bad( const Bad& )

我把那个类命名为Bad,因为它真的很糟糕,除了对别人的代码进行破坏之外,做这件事没有任何意义。

不要使用不复制的复制构造函数,

  • 实现一个移动构造函数来移动,或者

  • 实现一个普通的复制构造函数来复制,或者

  • 改变类设计,例如使用共享所有权


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接