【问题标题】:Isn't it bad to have a copy constructor making shallow copies?让复制构造函数制作浅拷贝不是很糟糕吗?
【发布时间】:2021-11-22 16:13:40
【问题描述】:

假设我已经定义了一些类BC

现在,让我们考虑以下类:

class A{
public:
    int att1;
    int att2;
    std::shared_ptr<B> b_ptr;
    std::shared_ptr<C> c_ptr;
    A(const A& a);
};

现在,问题是如何实现复制构造函数。

目前在代码中,这就是正在做的事情:

A::A(const A& a){
    att1 = a.att1;
    att2 = a.att2;
    b_ptr = a.b_ptr;
    c_ptr = a.c_ptr;
}

最后两行将调用std::shared_ptr的复制构造函数,并将底层指针的所有者数加1。

但是,让一个复制构造函数来做这件事不是一个坏主意吗?这意味着在调用复制构造函数的任何地方,它实际上都会与初始对象有关系,因为BC 类的对象实际上并没有被复制,而只有指针被复制。

我意识到,由于使用了shared_ptr,至少这不会造成任何内存泄漏。

这是一种常见的做法吗?有任何正当理由这样做吗?还是我应该一起避免?

【问题讨论】:

  • shared_ptr 用于共享所有权。所以是的,你打算像那样使用它们。如果是错的,为什么类的开头是shared_ptr
  • 它是否“坏”是您必须做出的决定。深复制和浅复制都有自己的位置,由您决定 A 的性质决定了一个副本是应该只复制指针还是同时复制指向的对象。
  • Nit:你应该使用成员初始化列表 A::A(...) : att1(a.att1), ... { } 而不是赋值。注意b_ptr = a.b_ptr 不调用副本constructor,而是调用副本assignment。 (要调用复制构造函数,您需要使用初始化列表......这就是您应该使用它的原因。)
  • Re:“如何实现复制构造函数”:在这种情况下,没有什么需要做的。问题中的代码与编译器生成的复制构造函数完全相同,但效率稍低(因为它不使用成员初始值设定项列表)。所以把它放在一边。编译器会帮你做的。

标签: c++ pointers smart-pointers


【解决方案1】:

假设A类是Person类,B类是car类。

Person类的副本指向同一辆原车可以吗?是的

--- 所以只要复制一个指针就可以了。

当 Person 的最后一个副本被销毁时,汽车被销毁可以吗?是的

--- 所以只复制智能指针就可以了。

类 Person 的副本可以得到自己的汽车副本吗?是的

--- 所以复制整个指向的对象也是可以的。

所有这些语义都可以是有效的。您可以根据自己的逻辑选择哪一个。

【讨论】:

    【解决方案2】:

    最后两行将调用std::shared_ptr的复制构造函数,并将底层指针的所有者数量加1。

    实际上,它们调用的是复制赋值运算符,而不是复制构造函数。

    但是,让复制构造函数来做这件事不是一个坏主意吗?

    不,因为shared_ptr 意味着要像这样被复制。跨多个实例共享指针的所有权是其唯一目的。

    这意味着在调用复制构造函数的任何地方,它实际上都会与初始对象有关系,因为BC 类的对象实际上并没有被复制,而只有指针被复制。

    没错,是的。这就是 shared_ptr 专门设计的用途。

    这是一种常见的做法吗?

    在这种情况下,是的。但如果改用原始指针,则不会。

    【讨论】:

      【解决方案3】:

      要回答主题行中的问题,复制构造函数制作浅拷贝既不是好事也不是坏事。

      在某些用例中,深拷贝(在复制的意义上创建所有包含对象的克隆)是可取且正确的。在其他用例中,浅拷贝(不克隆包含的对象)是可取且正确的。因此,作为类的设计者,您可以决定哪些复制语义是合适的。

      在您的特定示例中,您的类A 包含std::shared_ptr&lt;B&gt;std::shared_ptr&lt;C&gt;std::shared_ptr 的目的是提供托管对象生命周期的共享所有权,即复制(或复制分配)@ 987654325@ 表示副本负责管理与原始 std::shared_ptr&lt;X&gt; 相同的 X 的生命周期,而不是以某种方式克隆托管的 X 并创建一个管理克隆的 shared_ptr&lt;X&gt;

      因此,您示例中的问题不在于复制A 的实例不会对其管理的BC 进行深层复制。假设您实际上是在寻求深拷贝,那么问题在于您选择使用shared_ptr - 您选择了错误的工具(然后需要编写代码来进行深拷贝)。

      顺便说一下,你的复制构造函数

      A::A(const A& a){
        att1 = a.att1;
        att2 = a.att2;
        b_ptr = a.b_ptr;
        c_ptr = a.c_ptr;
      }
      

      也不做深拷贝。这也是不必要的,因为编译器会为您生成一个具有相同净效果的;

      A::A(const A& a): att1(a.att1), att2(a.att2), b_ptr(a.b_ptr), c_ptr(a.c_ptr)
      {}
      

      这使用std::shared_ptr&lt;B&gt;std::shared_ptr&lt;C&gt; 的复制构造函数,因此与您的实现不同,即首先默认构造b_ptrc_ptr,然后使用std::shared_ptr::operator=() 分配它们。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-09
        • 1970-01-01
        • 1970-01-01
        • 2019-11-13
        • 1970-01-01
        相关资源
        最近更新 更多