Однако возникла необходимость в нескольких наследниках добавить члены типа wstring. В этом случае использование memcpy() гарантирует множество приятных впечатлений, разумеется.
Вопрос: как можно реализовать выборочное клонирование, учитывающее наличие в членах класса сложных вещей типа wstring, ну или хотя бы указателей на уже инициализированные структуры данных?
Очень, очень хочется обойтись без поэлементного копирования — классов-наследников в проекте порядка пятидесяти и все они довольно толстые.
Re: "умное" клонирование экземпляров
От:
Аноним
Дата:
22.12.08 13:17
Оценка:
Здравствуйте, BlaineMono, Вы писали:
BM>Очень, очень хочется обойтись без поэлементного копирования — классов-наследников в проекте порядка пятидесяти и все они довольно толстые.
Здравствуйте, BlaineMono, Вы писали:
BM>Вопрос: как можно реализовать выборочное клонирование, учитывающее наличие в членах класса сложных вещей типа wstring, ну или хотя бы указателей на уже инициализированные структуры данных?
Здравствуйте, BlaineMono, Вы писали:
BM>Очень, очень хочется обойтись без поэлементного копирования — классов-наследников в проекте порядка пятидесяти и все они довольно толстые.
Можно попробовать использовать поэлементное копирование там, где это действительно нужно.
class CControl
{
protected:
virtual void OnAssign(CControl* source) {}
};
void CControl::Assign(CControl* source)
{
if (typeid(*this) != typeid(*source))
return;
memcpy(this, source, source->size_of);
OnAssign(source);
}
class CControl2: public CControl
{
protected:
virtual void OnAssign(CControl* source) { pp = &p; }
private:
int p;
int* pp; // pp points to p
};
Для wstring и других классов можно использовать static_cast и in-place new.
Х>а что мешает сделать метод Assign виртуальным и переопределить его только у тех наследников, для которых актуально более "умное" поведение?
Я когда-то так делал.
возникают нюансы:
class A {
virtual void operator=( const A& );
};
class B : public A {};
class C : public A {};
A a; B b; C c;
c = b; // проходит на ура, хотя вряд ли так задумывалось программистом.
Ну и внутри A::operator=(const A&) сложно узнать правильный sizeof( *this )
Так что я бы не парился и оставил реализацию оператора присваивания компилятору, за исключением случаев, где необходимо что-то особенное.
Благо нормальный компилятор копирование сложной структуры из POD приводит к проинлайненому memcpy,
а если там не-POD, то тогда всякие memcpy -- это именно грабли.
Здравствуйте, BlaineMono, Вы писали:
BM>Очень, очень хочется обойтись без поэлементного копирования — классов-наследников в проекте порядка пятидесяти и все они довольно толстые.
А что тебя смущает в поэлементном копировании?
Не хочется вручную писать? Так и не надо. По умолчанию компилятор сам за тебя всё сделает. Оператор = определён и публичен, если явно не сказать иное.
Или ты боишься, что сгенерированный код этих 50 операторов окажется очень громоздким? Ну, если для микроконтроллеров пишешь, тогда да. Но тогда нужно и RTTI отключать, оно тоже место занимает.
Хочу заметить, что данный код в общем случае некорректен — нельзя копировать содержимое классов просто копируя байты, если это только не POD. Удивительно, как это у вас вообще работает. Неужели во всех этих пятидесяти классах нет ни одного члена с нетривиальным оператором присваивания? Что это за архитектура такая с 50 POD'ами и виртуальным наследованием?
struct A
{
vector items;
void AssignAndEnsureCrash (const A& source)
{
// После выполнения этой прекрасной строчки указатели в this->items и source.items
// будут указывать на один и тот же блок памяти. Догадайтесь, что будет когда контейнеры
// начнут удалять свои элементы.
memcpy(this, &source, sizeof(*this));
}
};