我经常遇到问题,我必须扩展编译器生成的复制构造函数。例如:
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;
}
这将使我的代码更安全,更易于维护。不幸的是我不知道这种可能性。有没有?
答案 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和向下转发。