Создание копии объекта абстрактного базового класса - PullRequest
5 голосов
/ 21 января 2011

Если у меня есть указатель на объект, который является производным от абстрактного базового класса (поэтому я не могу создать новый объект этого класса), и я хочу сделать глубокую копию указанного объекта, есть ли более краткий способ достигнув этого, чем заставить абстрактный базовый класс создать новую чисто виртуальную copy функцию, которую должен реализовать каждый наследующий класс?

Ответы [ 3 ]

10 голосов
/ 21 января 2011

Нет, но метод copy не должен быть болезненным:

class Derived : public Base
{
  public:
    Base *copy() const
    {
        return new Derived(*this);
    }
};

(при условии, что у вас уже есть конструктор копирования, который, если вам нужна глубокая копия, у вас будет).

1 голос
/ 21 января 2011

Некоторое время назад кто-то в comp.lang.c ++ спросил, как автоматически создать функцию clone (). Кто-то еще представил идею, на которой я расширил. Ничто из этого не является проверенным кодом, и я никогда не пробовал его ... но я думаю, что это работает: http://groups.google.com/group/comp.lang.c++/browse_thread/thread/c01181365d327b2f/9c99f46a8a64242e?hl=en&ie=UTF-8&oe=utf-8&q=comp.lang.c%2B%2B+noah+roberts+clone&pli=1

1 голос
/ 21 января 2011

Предложенный «экземпляр», чаще называемый «клон», является нормальным подходом.Альтернативой может быть фабрика и диспетчеризация с использованием rtti для поиска правильного обработчика, который затем вызывает конструктор копирования для производного типа.

struct Abc
{
    virtual void who() const = 0;
};

struct A : Abc
{
    virtual void who() const { std::cout << "A" << std::endl;}
};

template<class T>
Abc* clone(Abc* abc)
{
    T* t = dynamic_cast<T*>(abc);
    if (t == 0)
        return 0;
    return new T(*t);
}

struct B : Abc
{
    virtual void who() const { std::cout << "B" << std::endl;}
};

typedef Abc* (*Cloner)(Abc*);

std::map<std::string, Cloner> clones;

void defineClones()
{
    clones[ typeid (A).name() ] = &clone<A>;
    clones[ typeid (B).name() ] = &clone<B>;
}


Abc* clone(Abc* abc)
{
    Abc* ret = 0;
    const char* typeName  = typeid(*abc).name();
    if (clones.find(typeName) != clones.end())
    {
        Cloner cloner = clones[typeName];
        ret = (*cloner)(abc);
    }
    return ret;
}
void test ()
{
    defineClones();
    Abc* a = new A;
    Abc* anotherA = clone(a);
    anotherA->who();

    Abc* b = new B;
    Abc* anotherB = clone(b);
    anotherB->who();
}

Хотя вышеприведенное работает, просто факт, что он использует rtti, будет достаточным дляубедить большинство идти нормальным подходом.Однако, если есть причина, препятствующая изменениям базового класса, это может быть полезно.

Это эффективно?Предельные затраты на добавление нового типа по-настоящему однострочны.Суть в том, что будет легко забыть добавить эту строку с каждым новым классом.Или вы можете видеть положительным моментом то, что весь код клона находится в одном файле, и нам не нужно изменять поддерживаемую иерархию для его обработки.

...