Imagine these classes:
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();
// ...
}
};
Then constructing a Derived
instance would result in
Base::Base
Derived::Derived
Derived::init
Base::init
Derived::check
Base::check
which is exactly what I want to achieve. It satisfies these requirements:
Base
definesinit()
with operations common to all subclasses and should be used right after construction of whole object (and only there)init()
can containvirtual
functions inside, but since it should be called only in the final constructor, it should not cause any harmcheck()
can be called any time, not only frominit()
(it should be independent from it), and should always perform all checking, not only those related to the subclass
My best approach so far, as above, was to use protected constructor with flag that avoids calling "incomplete" Base::init()
because of virtual functions do not work in superclass constructor. (Without the flag, Base::check()
would be called twice.)
My question is: isn't there a better, preferably somehow standard technique, that deals with calling virtual routines after whole object is initialized (pardon for vague terminology)? And of course without requiring users to call init()
explicitly (it should stay protected).
One possible use case (mine): Base
stands for e.g. an array of general mathematical formulas which must satisfy several constraints. Derived
(i.a.) restricts these constraints, add some, can override some particular checks, but mostly still use these from Base
. E.g. Base::check_formulas()
applies check_formula(f)
to every f
, and Derived
needs to override only check_formula
function.
EDIT:
As it is better to avoid virtual functions inside constructors at all, it appears not to be possible to achieve virtual function call from within the object itself, so the object must be constructed "externally" before calling these functions.
Both @StoryTeller and @Caleth suggests deal with this issue, either via dynamic allocation and pointer, or via a function with stack allocation (which is OK with move semantics).
Both of them inspired me to this solution, which is similar to @Caleth's as I found it more simple and straightforward:
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();
// ...
}
};
Usage:
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);
Output:
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
Any other suggestions?