Это может произойти немного позже после других ответов, но я все равно попробую.
Вы можете реализовать это безопасно и без изменения производных классов . Однако вам нужно будет изменить на всех этих классов, что может быть намного хуже, в зависимости от вашего сценария. Если вы все еще проектируете, то это может быть жизнеспособной альтернативой.
В принципе, вы можете применить любопытно повторяющийся шаблон шаблона и ввести код инициализации после вызова конструктора. Кроме того, если вы сделаете это так, как я написал ниже, вы даже сможете защитить load
от повторного вызова.
struct Loader {
int get(int index) { return 0; }
};
struct Base {
virtual ~Base() {} // Note: don't forget this.
protected:
virtual void load( Loader & ) = 0;
};
struct Derived : public Base {
int value;
protected:
void load( Loader &l ) {
value = l.get(0);
}
};
template<typename T>
class Loaded : public T
{
public:
Loaded () {
Loader l; T::load(l);
}
};
int main ( int, char ** )
{
Loaded<Derived> derived;
}
Честно говоря, я бы рассмотрел альтернативный дизайн, если вы можете. Переместите код из load
в ваши конструкторы и предоставьте загрузчик в качестве ссылочного аргумента по умолчанию следующим образом:
struct Derived : public Base {
Derived ( Loader& loader = Loader() ) { ... }
};
Таким образом, вы полностью избегаете проблемы.
Сводка : ваш выбор следующий:
- Если вы не ограничены внешними ограничениями и не имеете обширной базы кода в зависимости от этого, измените свой дизайн на что-то более безопасное.
- Если вы хотите сохранить
load
таким, какой он есть, и не слишком менять свои классы, но готовы платить цену за изменение всех экземпляров, примените CRTP, как предложено выше.
- Если вы настаиваете на большей обратной совместимости с существующим клиентским кодом, вам придется изменить свои классы на использование PIMPL, как предлагали другие, или жить с существующей проблемой.