Как я могу расширить созданный компилятор конструктор копии
Я часто сталкиваюсь с проблемой, что я должен расширить созданный компилятор конструктор копии. Пример:
class xyz;
class C
{
...
int a, b, c;
std::set<int> mySet;
xyz *some_private_ptr;
};
Предположим, что some_private_ptr
следует копировать только при определенных условиях. Для других условий он должен быть reset до 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;
}
Это сделает мой код более безопасным и простым в обслуживании. К сожалению, я не знаю такой возможности. Есть ли?
Ответы
Ответ 1
В тот момент, когда вы определяете свою собственную копию ctor, компилятор не беспокоится о том, чтобы создать ее для вас. К сожалению, это означает, что вы должны делать всю работу ноги самостоятельно! Вы могли бы группировать элементы в какую-то структуру impl_
внутри вашего класса, а затем полагаться на копию ctor для этого.
например:
class xyz;
class C
{
struct impl_
{
int a, b, c;
std::set<int> mySet;
xyz *some_private_ptr;
};
impl_ data;
};
теперь в вашей копии ctor
C::C(const C &other) : data(other.data)
{
// specific stuff...
}
Ответ 2
Самый простой способ - ввести базовый класс:
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) (хотя только иногда желательно) вы можете "продвигать" отдельных членов в защищенные или общедоступные не затрагивая других участников:
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;
}
Ответ 3
Проблема в том, что ваш класс пытается сделать слишком много. Либо используйте ресурс, либо управляйте ресурсом. Вы не выполняете оба, никогда, потому что ваш код станет небезопасным, кучей провалов. И это нехорошо.
Вам нужно создать класс, который управляет ресурсом, который копируется только при определенных условиях. Вы действительно не расширили свои условия и почему они там в первую очередь (что ужасно странный способ "скопировать" данные, вы уверены, что это лучший маршрут?), Но это было бы что-то например:
// 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 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 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>
{
// ...
};
Ответ 4
Я бы сказал, создайте умный указатель, который обрабатывает копирование, а затем используйте его как член вашего класса.
Эти коды могут дать вам представление:
В зависимости от того, как инициируется конструктор базового вызова, конструкторы-члены будут вызываться одинаково. Например, пусть начнется с:
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
Enjoy.
Ответ 5
Вы можете разместить свой специальный член в базовом классе, например:
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 и downcast.