Чисто дублировать экземпляр базового класса или подкласса в C ++? - PullRequest
2 голосов
/ 23 октября 2010

В тривиальном примере иерархии наследования:

class Food
{
    virtual ~Food();
};

class Fruit : public Food
{
    virtual ~Fruit();
};

class Apple: public Fruit
{
    virtual ~Apple();
}

class Vegetable: public Food
{
    virtual ~Vegetable();
}

Я хочу создать метод, который может клонировать объект из его экземпляра подкласса или базового класса:

Apple* apple1 = new Apple();
Apple* clone1 = apple1->clone();

Food* food1 = apple1;
Apple* clone2 = food1->clone();

Я вижу несколькоВозможные решения проблемы:

  • Используйте полиморфизм для создания виртуальной функции, которая явно определена для каждого подкласса.

  • Используйте шаблонный фабричный методвзять экземпляр класса и тип подкласса для создания нового экземпляра.

  • Зарегистрируйте идентификатор для каждого класса и подкласса, который хранится в другом месте, для поиска, какой подкласс создать наВызов функции клона базового класса.

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

Однако я очень открыт для любых предложений, есть ли лучшие способы сделать это?

Ответы [ 2 ]

2 голосов
/ 23 октября 2010

Вы можете использовать CRTP для автоматической реализации метода клонирования.

template<typename T, typename Derive> class CloneImpl : public Derive {
public:
    virtual Derive* clone() {
        return new T(static_cast<const T&>(*this));
    }
};
class Food {
public:
    virtual Food* clone() = 0;
    virtual ~Food() {}
};
class Fruit : public Food {
};
class Dairy : public Food {
};
class Apple : public CloneImpl<Apple, Fruit> {
};
class Banana : public CloneImpl<Banana, Fruit> {
};
class Cheese : public CloneImpl<Cheese, Dairy> {
};
class Milk : public CloneImpl<Milk, Dairy> {
};

В этом случае вы всегда можете вызвать Clone (), чтобы скопировать текущий объект с новым выделением в куче, и вам не нужно реализовывать его снова в любом классе. Конечно, если ваша семантика клонирования должна отличаться, вы можете просто изменить функцию.

Мало того, что CRTP может реализовать clone () для вас, он может даже сделать это между различными иерархиями наследования.

0 голосов
/ 23 октября 2010

Ваш последний пример, ...

Food* food1 = dynamic_cast<Food*>(apple1);
Apple* clone2 = f1->clone();

... не будет работать, даже если исправлена ​​ошибка орфографии. Вам нужен актерский состав другим способом:

Food* food1 = apple1;
Apple* clone2 = dynamic_cast<Apple*>( f1->clone() );

Кроме того, практическим решением клонирования в C ++ является определение макроса:

#define YOURPREFIX_IMPLEMENT_CLONING( Class )                       \
    virtual Class*                                                  \
        virtualCloneThatIsUnsafeToCallDirectly() const              \
    {                                                               \
        assert( typeid( *this ) == typeid( Class ) );               \
        return new Class( *this );                                  \
    }                                                               \
                                                                    \
    OwnershipPtr< Class >                                           \
        clone() const                                               \
    {                                                               \
        return OwnershipPtr< Class >(                               \
            virtualCloneThatIsUnsafeToCallDirectly()                \
            );                                                      \
    }

... где OwnershipPtr может быть, например, std::auto_ptr.

Тогда все, что вам нужно сделать, это поместить вызов макроса в каждый класс, который должен поддерживать клонирование. Простой.

Также возможно реализовать многоразовое клонирование с помощью шаблонов, но это сложнее как для реализации, так и для использования. Вы можете прочитать об этом в моем блоге "3 способа смешивания в общей реализации клонирования" . Вывод, однако, заключается в том, что макрос является наиболее практичным.

...