Здравствуйте, mjau, Вы писали:
M>Ааээ а чем это отличается от просто виртуальных функций? В наследниках тоже будут свои реализации foo() и bar() и их тоже надо будет ифдефить. На оставленные без деклараций тела компилятор будет ругаться. В чем выигрыш?
Отличается тем, что, допустим, тебе эти foo(), bar() для чего-то ещё нужны, сами по себе.
И заодно, через них выражается реализация B().
Выигрыш в том, что B() ты убираешь малой кровью.
К>>// а эта на CRTP
M>А CRTP работает дальше одного наследника? А как?
У классов с виртуальными функциями в рантайме в объекте хранится vfptr на актуальную vtbl, а у CRTP — в шаблон класса протаскивается параметр — финальный класс.
Единственная сложность — это то, что нефинальные классы должны быть шаблонами.
template<class TActual>
class Base
{
public:
TActual & self() { return *static_cast<TActual *>(this); }
TActual const& self() const { return *static_cast<TActual const*>(this); }
// паттерн Шаблонный Метод: "виртуально" вызываем методы наследника
void foo()
{
self().bar();
self().buz();
}
void bar() {} // "виртуальная" функция, которую наследник может переопределить, перегрузив
//void buz(); // "чисто виртуальная" функция - наследник обязан её определить
};
template<class TActual>
class Intermediate : public Base<TActual>
{
void bar() {}
void buz() {}
};
class Final : public Intermediate<Final>
{
void bar() {}
};
CRTP вовсю используется в ATL и WTL. Причём используется довольно стройно и последовательно. Так что можешь познакомиться с этой идиомой практически.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>