Ответ 1
Способ сделать это с помощью CRTP:
class A {
public:
virtual std::string Serialize();
virtual void Deserialize(std::string);
virtual A* Clone() = 0;
};
template <class T>
class HelperA : public A {
T* Clone() override
{
std::string s = Serialize();
T* t = new T();
t->Deserialize(s);
return t;
}
};
class B : public HelperA<B> {
public:
std::string Serialize() { ... }
void Deserialize(std::string) { ... }
};
Эти иерархии уровня 3 довольно распространены. В принципе, верхний класс - чистый интерфейс, как и раньше (обратите внимание: вы должны = 0 другие функции тоже). Средний класс использует CRTP-шаблон: он исчисляется на основе типизированного. идея состоит в том, что статический доступ к производному типу может автоматически реализовывать такие вещи, как Clone
. Тогда производный тип реализует любую реализацию, которая не может быть выполнена в общем случае.
Обратите внимание: наследуемый тип наследуется от класса CRTP. То, откуда происходит название (Curiously Recurring Template Pattern). Конечно, так как наследование транзитивно, B также наследует от A все еще, как изначально, позволяя такие же вещи.
Вот полный рабочий пример, который вы можете выполнить: http://coliru.stacked-crooked.com/a/8f2b201a06b5abcc. Я сохранил код в ответе как можно более похожий на вопрос, но в примере coliru есть несколько небольших, но важных отличий:
- использование указателей владения вместо необработанных указателей считается хорошей практикой в С++, а поскольку интеллектуальные указатели не ковариантны, это влияет на подписи
- правильное использование = 0 и переопределение, а также const
- пример статического downcast, который является своего рода сигнатурой CRTP, которая не придумала ваш пример