如何扩展编译器生成的复制构造函数

RED*_*AIR 18 c++ copy-constructor

我经常遇到问题,我必须扩展编译器生成的复制构造函数.例:

class xyz;
class C
{
    ...
    int a, b, c; 
    std::set<int> mySet;
    xyz *some_private_ptr;
};
Run Code Online (Sandbox Code Playgroud)

假设,some_private_ptr只应在特定条件下复制.对于其他条件,应将其重置为NULL复制.所以我必须编写一个复制构造函数,如:

C::C(const C &other) : 
     a(other.a), 
     b(other.b), 
     c(other.c), 
    mySet(other.mySet)
{      
   if(CanCopy(other.some_private_ptr)) // matches condition
      some_private_ptr = other.some_private_ptr;
   else
      some_private_ptr = NULL;
}
Run Code Online (Sandbox Code Playgroud)

问题是该类可能有许多数据成员,并且当我添加数据成员时,我很可能忘记更新复制构造函数.如果我能写的话,那将是非常好的.

C::C(const C &other) :
   C::default_copy(other)
{      
   if(CanCopy(other.some_private_ptr)) // matches condition
      some_private_ptr = other.some_private_ptr;
   else
      some_private_ptr = NULL;
}
Run Code Online (Sandbox Code Playgroud)

这将使我的代码更安全,更容易维护.不幸的是我不知道这种可能性.有没有?

Fre*_*urk 18

最简单的方法是引入一个基类:

class xyz;

struct CDetail {
  //...
  int a, b, c; 
  std::set<int> mySet;
  xyz *some_private_ptr;
};

struct C : private CDetail {
  C(C const &other)
  : CDetail(other)
  {
    if (!CanCopy(other.some_private_ptr))
      some_private_ptr = 0;
    // opposite case already handled
  }
};
Run Code Online (Sandbox Code Playgroud)

这是滥用继承的程度,但优于嵌套"impl"类的优点是1)您可以访问每个成员作为"名称"而不是"data.name"(减少重构时的代码更改),以及2) (虽然有时只是需要)你可以"促进"个人成员受保护或公开而不影响其他成员:

struct C : private CDetail {
protected:
  using CDetail::a;
};

struct D : C {
  void f() {
    cout << a;
  }
};

int main() {
  D d;
  d.f();  // D can access 'a'
  cout << d.a;  // main cannot access 'a'
  return 0;
}
Run Code Online (Sandbox Code Playgroud)


Nim*_*Nim 16

在您定义自己的副本ctor的那一刻,编译器不会为您生成一个.不幸的是,这意味着你必须自己做所有的腿部工作!您可以将成员分组到类中的某种impl_结构中,然后依赖于复制ctor.

例如:

class xyz;
class C
{
  struct impl_
  {
    int a, b, c; 
    std::set<int> mySet;
    xyz *some_private_ptr;
  };

  impl_ data;
};
Run Code Online (Sandbox Code Playgroud)

现在在你的副本ctor

C::C(const C &other) : data(other.data)
{
 // specific stuff...      
}
Run Code Online (Sandbox Code Playgroud)


GMa*_*ckG 12

这里的问题是你的班级试图做太多.无论使用的资源,或管理的资源.你不会同时做这两件事,因为你的代码会成为一堆不安全的污点.这不好.

您需要设计一个管理资源的类,该资源仅在特定条件下复制.你还没有真正扩展这些条件以及为什么它们首先出现在那里(这是一种非常奇怪的"复制"数据的方式,你确定这是最好的路线吗?),但它确实是像这样:

// pointer to your condition member (not sure if this is even needed,
// is this condition knowable via the pointer alone? you get the point)
template <typename T, typename D, class Tag = void>
class copy_conditional_ptr
{
public:
    copy_conditional_ptr(bool (D::*condition)(T*) const, T* value = 0) :
    mCondition(condition),
    mValue(value)
    {}

    // here's where the unique copy-semantics go
    copy_conditional_ptr(const copy_conditional_ptr& other) :
    mCondition(other.mCondition),
    mValue(do_copy(other.mValue) ? other.mValue : 0)
    {}

    // other stuff for a smart pointer,
    // copy-and-swap, etc...

protected:
    // protected because it's meant to be a base class
    ~copy_conditional_ptr()
    {
        // whatever
    }

private:
    bool do_copy(T* value) const
    {
        const D& self = static_cast<const D&>(*this);
        return (self.*mCondition)(other.value);
    }

    bool (D::*mCondition)(T*) const;
    T* mValue;
};
Run Code Online (Sandbox Code Playgroud)

然后你像这样使用它:

class xyz;

class C : private copy_conditional_ptr<xyz, C>
{
public:
    C() :
    /* others, */
    copy_conditional_ptr(&C::CanCopy)
    {}

private:
    int a, b, c; 
    std::set<int> mySet;
};
Run Code Online (Sandbox Code Playgroud)

让管理层对其他同学自动化.标签是这样你可以在同一个类中有多个:

class C : private copy_conditional_ptr<xyz, C, struct C_first>,
            private copy_conditional_ptr<xyz, C, struct C_second>
{
    // ...
};
Run Code Online (Sandbox Code Playgroud)

  • 如果可能的话,我会更多地投票.这个答案是根本原因,而不仅仅是解决症状. (5认同)
  • @Red:你为什么要写一个拷贝构造函数呢?整点都正确地将自定义复制语义留给资源*管理*,因此您不必自己完成.其他解决方案试图通过反转这种关系来解决您的问题,这是非常不正确的. (2认同)
  • @Fred:**哦**,呃.嗯,似乎确实没有办法,嗯? (2认同)