2011-01-21 5 views
4

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

+0

Что дает вам идею, что это неэффективно? – GManNickG

+0

@GMan «неэффективен», как в том, что требует много работы для программиста, по сравнению с, скажем, отличным однострочным трюком или чем-то, что выполнит то же самое (как я иногда обнаруживаю, это случай в Stackoverflow) – wrongusername

+0

Слово вы хотите, чтобы это было «кратким». Вероятно, вам следует уточнить это в своем вопросе. И дайте понять, что вы подразумеваете ремонтопригодность и чистоту, а не производительность. – GManNickG

ответ

9

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

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

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

+1

Вы хотите, чтобы метод 'copy' был чистым виртуальным в базовом классе, поэтому компилятор скажет вам, не забыли ли вы его предоставить. –

+1

@Mark - это только обеспечивает ложное чувство безопасности, так как любые внуки тоже могли бы забыть. –

+0

@Noah, тоже правда. Но это только одна причина, чтобы избежать внуков, я мог бы предоставить других. –

1

Предлагаемый «экземпляр», обычно называемый «клон», является обычным подходом. Альтернативой может быть фабрика и отправка с использованием 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 будет достаточно, чтобы убедить большинство идти нормальный подход. Однако, это была причина предотвращения изменений в базовом классе, это может быть полезно.

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

1

Некоторое время назад кто-то в 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

Смежные вопросы