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

时间:2011-01-31 16:51:48

标签: c++ copy-constructor

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

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

假设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;
}

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

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;
}

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

5 个答案:

答案 0 :(得分: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
  }
};

这是滥用继承的程度,但优于嵌套“impl”类的优点是1)您可以以“名称”而不是“data.name”访问每个成员(减少重构时的代码更改), 2)(虽然只是有时候需要)你可以“促使”个人成员受到保护或公开without affecting other members

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;
}

答案 1 :(得分:16)

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

例如:

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

  impl_ data;
};

现在在你的副本中

C::C(const C &other) : data(other.data)
{
 // specific stuff...      
}

答案 2 :(得分: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;
};

然后你就这样使用它:

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;
};

让管理层自动完成其余的课程。标签是这样你可以在同一个类中有多个:

class C : private copy_conditional_ptr<xyz, C, struct C_first>,
            private copy_conditional_ptr<xyz, C, struct C_second>
{
    // ...
};

答案 3 :(得分:0)

我想创建一个处理复制的智能指针,然后将其用作类的成员。 这些代码可能会给你一个想法:

根据基本调用构造函数的启动方式,将以相同的方式调用成员的构造函数。例如,让我们从:

开始
struct ABC{
    int a;
    ABC() : a(0)    {   printf("Default Constructor Called %d\n", a);   };

    ABC(ABC  & other )  
    {
        a=other.a;
        printf("Copy constructor Called %d \n" , a ) ;
    };
};

struct ABCDaddy{
    ABC abcchild;
};

你可以做这些测试:

printf("\n\nTest two, where ABC is a member of another structure\n" );
ABCDaddy aD;
aD.abcchild.a=2;

printf( "\n Test: ABCDaddy bD=aD;  \n" );
ABCDaddy bD=aD; // Does call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is  called)

printf( "\n Test: ABCDaddy cD(aD); \n" );
ABCDaddy cD(aD);    // Does call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is  called)

printf( "\n Test: ABCDaddy eD; eD=aD;  \n" );
ABCDaddy eD;
eD=aD;          // Does NOT call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is not called)

输出:

Default Constructor Called 0

Test: ABCDaddy bD=aD;
Copy constructor Called 2

Test: ABCDaddy cD(aD);
Copy constructor Called 2

Test: ABCDaddy eD; eD=aD;
Default Constructor Called 0

享受。

答案 4 :(得分:0)

您可以将特殊成员放在基类中,例如:

class xyz;

class SpecialCopyXYZ
{
public:
    SpecialCopyXYZ() = default;
    SpecialCopyXYZ(const SpecialCopyXYZ& rhs)
    {
       if (CanCopy(other.some_private_ptr)) {
          some_private_ptr = other.some_private_ptr;
       } else {
          some_private_ptr = nullptr;
       }
    }

    // SpecialCopyXYZ& operator=(const SpecialCopyXYZ& rhs)

protected:
    xyz *some_private_ptr = nullptr;
};


class C : private SpecialCopyXYZ
{
public:
    C(const C &other) = default;
private:
    int a, b, c; 
    std::set<int> mySet;
};

如果SpecialCopyXYZ需要C数据,您可以使用CRTP和向下转发。