【问题标题】:deep copy of object which hold references to other objects包含对其他对象的引用的对象的深层副本
【发布时间】:2011-12-09 11:07:02
【问题描述】:

我有一个“sum”类,它包含两个对现有整数的引用(比如说)。我想创建一个深度复制整数的“复制”方法。由于智能指针,我认为我永远不必在我的代码中手动 delete 对象,但我必须在这个解决方案中。此外,对于如此微不足道的任务(我需要重复几节课)来说,它太复杂了。有没有更直接的解决方案?

注意:我不想为每个对象添加一个 bool 成员(标志)以确定是否必须删除整数(在我的情况下,它的开销并不比析构函数中的 std::set 检查开销更好)

#include <set>

struct sum {
   const int &a, &b;
   static std::set<const int*> allocated_ints;

   sum(const int& a, const int&b): a(a), b(b) {}

   sum copy() const {
       sum res(*new const int(a), *new const int(b));
       allocated_ints.insert(&res.a);
       allocated_ints.insert(&res.b);
       return res;
   }

   ~sum() {
       if (allocated_ints.count(&this->a)) {
           delete &this->a;
           delete &this->b;
           allocated_ints.erase(&this->a);
           allocated_ints.erase(&this->b);
       }
   }

};

std::set<const int*> sum::allocated_ints;

【问题讨论】:

  • 你不应该删除这些引用。
  • @rafak:你的代码引用了ints,这意味着它们被其他类“拥有”,你的类不应该删除它们。如果它们不被其他类“拥有”,那么您不应该通过引用来存储 ints。
  • 你在说什么智能指针?我在您的代码中看不到任何智能指针。 (引用不是智能指针,如果您是这样想的话。)
  • @John:我将我的用例作为对 Kerrek SB 答案的评论。从类中分配引用时,我需要删除引用。
  • @MooingDuck:int 最常由其他类拥有。在极少数情况下,它们不是(这就是我需要通过副本构建它们的原因)。

标签: c++ reference smart-pointers deep-copy


【解决方案1】:

我认为你混淆了两个不相容的概念。

如果您通过引用进行初始化,您应该引用其生命周期已经定义并且应该比您的对象更长的现有对象。

如果你想为你的对象创建一个副本,因为它引用了东西,你的副本也将引用那个东西。

如果您想拥有自己的动态提供的对象,您应该为此使用指针,并将它们作为指针获取(并在销毁时删除它们)。然后副本可以深度创建指向对象的副本(或者可以使用引用计数或 shared_ptr 共享它们)。

您实际上是在混合这两种情况,从而导致可能出现的问题:思考:

int main()
{
    const int x=5; //whatever it is
    Foo foo(x);
    // ...
} //danger here! ~Foo() will delete x

【讨论】:

    【解决方案2】:

    引用不会被深度复制,因为它们指向一个对象。因此,您修复的代码应如下所示:

    struct sum {
       const int &a, &b;
    
       sum(const int& a, const int&b): a(a), b(b) {}
    
       sum copy() const {
           sum res(a,b);
           return res;
       }
    
       ~sum() {
       }
    
    };
    

    【讨论】:

    • 但我需要副本来“引用”新对象。您的副本版本就像默认的副本构造函数。
    • @rafak 在这种情况下,更改对指针的引用(更好的智能指针),或删除引用(使它们成为对象)。以上是引用的复制方式。
    【解决方案3】:

    常量的“深层”副本有什么意义?无论如何,常量都将具有相同的值!所以只需复制(即别名)const-references:

    struct Foo
    {
      const int & n;
    
      Foo(const int & m) : n(m) { }
      Foo(const Foo & rhs) : n(rhs.n) { }
    
      Foo copy() const { Foo f(*this); /* ... */ return f; }
      // ...
    };
    

    如果您在从具有对局部变量的引用的函数返回副本时担心悬空引用,那么不要让类具有 const 引用,而是副本。这样,您自然而然地为您的班级提供了您似乎想要的复制语义。

    如果您认为您可以根据您的使用方式制作一个不拥有或变为拥有的混合体,那么我会说这是您应该避免的糟糕设计。确定您的班级是否拥有数据的所有权,然后继续使用它。

    【讨论】:

    • 它主要是一个参考语义。这是我的用例: int n=1;富富(n); /* 用 foo 计算,但直接操作 n /; if (foo.is_intersting()) Foo good_foo = foo.copy(); / 用 foo 继续计算,改变 n... */ 的值(所以引用的 const-ness 与我的问题不是很相关)。注意:“C”cmets 显示不好
    • 嗯,不知怎的,这对我来说看起来像是糟糕的封装。我要么将n 设为参数,foo.compute(n);,要么将修改n 的整个逻辑作为类的一部分。然后,一旦你达到了一个有趣的状态,你就可以直接复制对象。
    • 对于Foo func() {return Foo(1);} 来说,这是个坏主意。这将返回带有悬空引用的Foo,从而导致未定义的行为。该类应使其成为自己的本地副本,除非您可以保证ints 的范围比Foo 长。此外,他们不能“更改 n 的值”,因为他们将引用保留为 const
    • ...否则这真的很丑陋。您需要一个变量,您的代码的其他部分引用该变量,但您还希望制作该变量的存档副本。那不好。制作一个允许持久引用的容器可能会更好,如下所示:list&lt;int&gt; nvals; nvals.push_back(1); while(computing) { Foo current_foo(nvals.back()); if(good) { Foo good_foo(current_foo); nvals.push_back(nvals.back()); } 这不太正确,但您明白了:维护一个值列表,其中最后一个是当前值,之前的值是存档.
    • @MooingDuck:嗯,正如我所说,OP 的强耦合设计很尴尬,并导致了这些复杂性。我敢打赌,有一个更好的整体方法可以解决所有这些问题。
    猜你喜欢
    • 1970-01-01
    • 2012-01-21
    • 2016-02-15
    • 2021-11-06
    • 1970-01-01
    • 2020-11-20
    • 1970-01-01
    • 2015-12-28
    相关资源
    最近更新 更多