创建一个易于维护的复制构造函数

时间:2010-07-07 07:30:24

标签: c++ constructor deep-copy

考虑以下课程:

class A {

char *p;
int a, b, c, d;

public:
   A(const &A);
};

请注意,我必须定义一个复制构造函数才能执行“p”的深层复制。这有两个问题:

  1. 大多数字段都应该被复制。逐个复制它们很丑陋且容易出错。

  2. 更重要的是,每当向该类添加新属性时,都需要更新复制构造函数,这会造成维护噩梦。

  3. 我个人喜欢做类似的事情:

    A(const A &a) : A(a)
    {
       // do deep copy of p
       :::
    }
    

    因此,首先调用默认的复制构造函数,然后执行深层复制 不幸的是,这似乎不起作用。

    有没有更好的方法呢? 一个限制 - 我不能使用共享/智能指针。


    Sbi的建议很有意义。我想我会创建用于处理资源的包装类。我不想使用shared_ptr,因为升级库可能并非在所有平台上都可用(至少在标准发行版中没有,OpenSolaris就是一个例子)。

    我仍然认为如果你能以某种方式让编译器为你创建默认的构造函数/赋值运算符会很棒,你可以在它上面添加你的功能。我认为手动创建的复制构造函数/赋值运算符函数将是一个麻烦创建和维护的噩梦。因此,我个人的经验法则是不惜一切代价避免自定义复制构造函数/赋值运算符。

    感谢大家的回复和有用的信息,并对我的问题中的拼写错误表示抱歉。我是从手机上打字的。

9 个答案:

答案 0 :(得分:19)

根据经验:如果您必须手动管理资源,将每个资源包装到自己的对象中。

使用适当的复制构造函数将char*放入其自己的对象中,让编译器为A执行复制构造函数。 请注意,这也涉及分配和销毁,您在问题中未提及,但仍需要处理。
标准库有几种类型可供选择,其中包括std::stringstd::vector<char>

答案 1 :(得分:4)

char*替换为std::string

答案 2 :(得分:3)

始终使用RAII对象来管理非管理资源(如原始指针),并为每个资源使用一个RAII对象。一般来说,避免使用原始指针。在这种情况下,使用std::string是最佳解决方案。

如果出于某种原因无法做到这一点,那么就可以轻松地将部分复制到基类或成员对象中。

答案 3 :(得分:3)

您可以将可复制成员分成POD结构,并保留需要单独管理副本的成员。

由于您的数据成员是私有的,因此您的班级客户可以看不到。

E.g。

class A {

char *p;

struct POData {
    int a, b, c, d;
    // other copyable members
} data;

public:
   A(const &A);
};

A(const A& a)
    : data( a.data )
{
    p = DuplicateString( a.p );
    // other managed copies...
    // careful exception safe implementation, etc.
}

答案 4 :(得分:2)

你真的应该在这里使用智能指针。

这样可以避免重写复制构造函数和表示操作符(operator=)。

这两个都容易出错。

operator=常见错误正在以这种方式实施:

SomeClass& operator=(const SomeClass& b)
{
  delete this->pointer;
  this->pointer = new char(*b.pointer); // What if &b == this or if new throws ?

  return *this;
}

当一个人做错时失败:

SomeClass a;
a = a; // This will crash :)

智能指针已经处理了这些情况,显然不易出错。

此外,像boost::shared_ptr这样的智能指针甚至可以处理自定义的释放函数(默认情况下它使用delete)。在实践中,我很少遇到使用智能指针而不是原始指针的情况是不切实际的。

快速说明:boost智能指针类,仅限标头设计(基于模板),因此它们不需要额外的依赖项。 (有时,这很重要)你可以包括它们,一切都应该没问题。

答案 5 :(得分:0)

问题是,你真的需要一个带有深拷贝语义的指针吗?根据我的经验,答案几乎总是不行。也许您可以解释一下您的情景,因此我们可能会向您展示其他解决方案。

也就是说,this article描述了具有深拷贝语义的智能指针的实现。

答案 6 :(得分:0)

虽然我同意其他人的说法,你应该将指针包装在自己的RAII类中,让编译器合成复制构造函数,析构函数和赋值运算符,然后解决你的问题:声明(和定义)私有静态函数这将为不同的构造者做任何需要和共同的事情并从那里调用它。

答案 7 :(得分:0)

  

因此,首先调用默认的复制构造函数,然后执行深层复制。   不幸的是,这似乎不起作用。

     

有没有更好的方法呢?一个限制 - 我不能使用共享/智能指针。

如果我理解正确,您可以考虑使用初始化函数:

class A
{
    int i, j;
    char* p;

    void Copy(int ii, int jj, char* pp); // assign the values to memebers of A
public:
    A(int i, int j, char* p);
    A(const A& a);
};

A::A(int i, int j, char* p)
{
    Copy(i, j, p);
}

A::A(const A& a)
{
    Copy(a.i, a.j, a.p);
}

那就是说,你真的应该考虑使用RAII(这是人们不断推荐它的原因:))以获取额外的资源。

如果我不能使用RAII,我仍然更喜欢为每个成员创建复制构造函数和使用初始化列表(实际上,即使使用RAII,我也更喜欢这样做):

A::A(int ii, int lj, char* pp)
    : i(ii)
    , j(jj)
    , p( function_that_creates_deep_copy(pp) )
{
}

A::A(const A& a)
    : i(a.i)
    , j(a.j)
    , p( function_that_creates_deep_copy(a.p) )
{
}

这具有“显性”的优点,并且易于调试(您可以介入并查看它对每次初始化的作用)。

答案 8 :(得分:0)

除非您的类具有一个管理资源的功能,否则您永远不应该直接管理任何资源。始终使用某些描述的智能指针或自定义管理类。通常,如果可以,最好保留隐式复制构造函数。这种方法还可以轻松维护析构函数和赋值运算符。