Представьте себе эти классы:
class Base {
public:
Base() : Base(false)
{ }
virtual ~Base() = default;
void init()
{
cout << "Base::init" << endl;
check();
// ...
}
virtual void check()
{
cout << "Base::check" << endl;
// ...
}
protected:
Base(bool skip_init)
{
cout << "Base::Base" << endl;
if (!skip_init) init();
}
};
class Derived : public Base {
public:
Derived() : Base(true)
{
cout << "Derived::Derived" << endl;
init();
}
virtual ~Derived() = default;
void init()
{
cout << "Derived::init" << endl;
Base::init();
// ...
}
virtual void check() override
{
cout << "Derived::check" << endl;
Base::check();
// ...
}
};
Тогда создание экземпляра Derived
приведет к
Base::Base
Derived::Derived
Derived::init
Base::init
Derived::check
Base::check
, чего я и хочу достичь.Он удовлетворяет этим требованиям:
Base
определяет init()
с операциями, общими для всех подклассов, и должен использоваться сразу после создания целого объекта (и только * там) init()
может содержать virtual
функций внутри, но так как он должен вызываться только в конечном конструкторе, он не должен причинять никакого вреда check()
может бытьвызывается в любое время, не только с init()
(он должен быть независим от него), и всегда должен выполнять все проверки, а не только те, которые относятся к подклассу
Мой лучший подход на данный момент, как указано выше, должен был использовать защищенный конструктор с флагом, который избегает вызова «неполного» * 1028 *, потому что виртуальные функции не работают в конструкторе суперкласса.(Без флага Base::check()
будет вызван дважды.)
Мой вопрос: разве не существует лучшего, желательно каким-то стандартного метода, который имеет дело с вызовом виртуальных подпрограмм после целого объекта * 1033?* инициализирован (простите за расплывчатую терминологию)?И, конечно, не требуя от пользователей явного вызова init()
(он должен оставаться защищенным).
Один из возможных вариантов использования (мой): Base
обозначает, например, массив общих математических формул, которые должны удовлетворять нескольким ограничениям.,Derived
(ia) ограничивает эти ограничения, добавляет некоторые, может отменять некоторые конкретные проверки, но в основном все еще использует их из Base
.Например, Base::check_formulas()
применяется check_formula(f)
к каждому f
, а Derived
необходимо переопределить только check_formula
функцию.
EDIT :
Как естьЛучше вообще избегать виртуальных функций внутри конструкторов, кажется, что невозможно вызвать вызов виртуальной функции изнутри самого объекта, поэтому перед вызовом этих функций объект должен быть "внешним".
Оба @StoryTeller@Caleth предлагает решить эту проблему либо с помощью динамического выделения и указателя, либо с помощью функции с выделением стека (что нормально с семантикой перемещения).
Оба они вдохновили меня на это решение, которое похожедля @ Caleth'а, как мне показалось, более простым и понятным:
template <typename T, typename... Args>
T create(Args&&... args)
{
T t(forward<Args>(args)...);
t.init();
return t;
}
class Base {
public:
virtual ~Base() = default;
Base(const Base& rhs) = default;
Base(Base&& rhs) = default;
Base& operator=(const Base& rhs) = default;
Base& operator=(Base&& rhs) = default;
template <typename T, typename... Args>
friend T create(Args&&... args);
protected:
Base() : _arg(0)
{
cout << "Base::Base()" << endl;
}
Base(int arg) : _arg(arg)
{
cout << "Base::Base(int)" << endl;
}
virtual void init()
{
cout << "Base::init" << endl;
check();
// ...
}
virtual void check()
{
cout << "Base::check" << endl;
// ...
}
private:
int _arg;
};
class Derived : public Base {
public:
virtual ~Derived() = default;
template <typename T, typename... Args>
friend T create(Args&&... args);
protected:
Derived() : Base()
{
cout << "Derived::Derived()" << endl;
}
Derived(int arg) : Base(arg)
{
cout << "Derived::Derived(int)" << endl;
}
void init() override
{
cout << "Derived::init" << endl;
Base::init();
// ...
}
void check() override
{
cout << "Derived::check" << endl;
Base::check();
// ...
}
};
Использование:
cout << endl << "Base() ..." << endl;
Base b1 = create<Base>();
cout << endl << "Base(int) ..." << endl;
Base b2 = create<Base>(5);
cout << endl << "Derived() ..." << endl;
Derived d1 = create<Derived>();
cout << endl << "Derived(int) ..." << endl;
Derived d2 = create<Derived>(10);
Вывод:
Base() ...
Base::Base()
Base::init
Base::check
Base(int) ...
Base::Base(int)
Base::init
Base::check
Derived() ...
Base::Base()
Derived::Derived()
Derived::init
Base::init
Derived::check
Base::check
Derived(int) ...
Base::Base(int)
Derived::Derived(int)
Derived::init
Base::init
Derived::check
Base::check
Любые другие предложения?