"умное" клонирование экземпляров
От: BlaineMono  
Дата: 22.12.08 13:01
Оценка:
Задача — создать клон экземпляра произвольного класса-наследника базового класса.

Сейчас клонирование реализовано на уровне базового класса, втупую:

void CControl::Assign(CControl* source)
{
    if (typeid(*this) != typeid(*source))
        return;

    memcpy(this, source, source->size_of);

    //cut
}


Однако возникла необходимость в нескольких наследниках добавить члены типа wstring. В этом случае использование memcpy() гарантирует множество приятных впечатлений, разумеется.

Вопрос: как можно реализовать выборочное клонирование, учитывающее наличие в членах класса сложных вещей типа wstring, ну или хотя бы указателей на уже инициализированные структуры данных?

Очень, очень хочется обойтись без поэлементного копирования — классов-наследников в проекте порядка пятидесяти и все они довольно толстые.
Re: "умное" клонирование экземпляров
От: Аноним  
Дата: 22.12.08 13:17
Оценка:
Здравствуйте, BlaineMono, Вы писали:

BM>Очень, очень хочется обойтись без поэлементного копирования — классов-наследников в проекте порядка пятидесяти и все они довольно толстые.


А никуда без этого не денешься.
Re: "умное" клонирование экземпляров
От: Alexander G Украина  
Дата: 22.12.08 14:50
Оценка:
Здравствуйте, BlaineMono, Вы писали:

BM>Вопрос: как можно реализовать выборочное клонирование, учитывающее наличие в членах класса сложных вещей типа wstring, ну или хотя бы указателей на уже инициализированные структуры данных?


Через конструкторы копирования этих наследников
Русский военный корабль идёт ко дну!
Re: "умное" клонирование экземпляров
От: COFF  
Дата: 22.12.08 14:53
Оценка:
Здравствуйте, 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.
Re: "умное" клонирование экземпляров
От: Хвост  
Дата: 22.12.08 19:22
Оценка: +1 -1
Здравствуйте, BlaineMono, Вы писали:

BM>Задача — создать клон экземпляра произвольного класса-наследника базового класса.


а что мешает сделать метод Assign виртуальным и переопределить его только у тех наследников, для которых актуально более "умное" поведение?
People write code, programming languages don't.
Re[2]: "умное" клонирование экземпляров
От: K13 http://akvis.com
Дата: 23.12.08 12:10
Оценка:
Х>а что мешает сделать метод 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 -- это именно грабли.
Re: "умное" клонирование экземпляров
От: Кодт Россия  
Дата: 23.12.08 13:16
Оценка:
Здравствуйте, BlaineMono, Вы писали:

BM>
BM>void CControl::Assign(CControl* source)
BM>{
BM>    if (typeid(*this) != typeid(*source))
BM>        return;

BM>    memcpy(this, source, source->size_of);

BM>    //cut
BM>}
BM>

Если уж ты определяешь для наследников поле size_of, то почему бы вместо этой рукодельщины сделать честную виртуальную функцию присваивания?
void CControl::Assign(CControl* source)
{
    if(this is_superclass source) // на typeid, или ещё каким способом
        AssignImpl(source);
}

.....

/*virtual*/ void CSomeControl::AssignImpl(CControl* source)
{
    *this = *static_cast<CSomeControl*>(source);
}


Ха! Так ведь и ещё проще!!!
/*virtual*/ void CSomeControl::Assign(CControl* source)
{
    CSomeControl* the_source = dynamic_cast<CSomeControl*>(source);
    if(the_source)
        *this = *the_source;
}

И не надо забивать себе мозг стрельбой по памяти и сравнениями typeid'ов.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
Re: "умное" клонирование экземпляров
От: Кодт Россия  
Дата: 24.12.08 08:50
Оценка:
Здравствуйте, BlaineMono, Вы писали:

BM>Очень, очень хочется обойтись без поэлементного копирования — классов-наследников в проекте порядка пятидесяти и все они довольно толстые.


А что тебя смущает в поэлементном копировании?
Не хочется вручную писать? Так и не надо. По умолчанию компилятор сам за тебя всё сделает. Оператор = определён и публичен, если явно не сказать иное.
Или ты боишься, что сгенерированный код этих 50 операторов окажется очень громоздким? Ну, если для микроконтроллеров пишешь, тогда да. Но тогда нужно и RTTI отключать, оно тоже место занимает.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
Re: "умное" клонирование экземпляров
От: Аноним  
Дата: 29.12.08 12:17
Оценка:
BM>
BM>void CControl::Assign(CControl* source)
BM>{
BM>    if (typeid(*this) != typeid(*source))
BM>        return;

BM>    memcpy(this, source, source->size_of);

BM>    //cut
BM>}
BM>


Хочу заметить, что данный код в общем случае некорректен — нельзя копировать содержимое классов просто копируя байты, если это только не POD. Удивительно, как это у вас вообще работает. Неужели во всех этих пятидесяти классах нет ни одного члена с нетривиальным оператором присваивания? Что это за архитектура такая с 50 POD'ами и виртуальным наследованием?

struct A
{
    vector items;

    void AssignAndEnsureCrash (const A& source)
    {
        // После выполнения этой прекрасной строчки указатели в this->items и source.items 
        // будут указывать на один и тот же блок памяти. Догадайтесь, что будет когда контейнеры 
        // начнут удалять свои элементы.
        memcpy(this, &source, sizeof(*this));
    }
};
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.