I have a base class with an abstract virtual function fv()
which is only implemented in derived classes. I want to make sure that the fv()
of the derived classes is called in or immediately after the constructor.
However, when I call the virtual function in the constructor, I get a warning, and the code can’t be linked:
undefined reference to `A::fv()’
I also tried this:
class A{
A() {fx()};
virtual void fv() = 0;
void fx(fv());
}
class B : public A {
B():A();
void fv() {};
}
int main(int argc, char **argv) {
B::b{];
}
This code compiles, but when I run it I get:
pure virtual method called
terminate called without an active exception
Aborted
Of course, I can manually call fv()
in the constructor of B
, but this seems inelegant and not safe, because someone deriving a class from my A
might forget to call fv()
(which could lead to undefined behavior in my real code).
Is there an “automatic” way to make sure that the fv()
of derived classes will be called as soon as the object is instantiated?
8
You can’t call a virtual method inside the base class constructor. The derived class object doesn’t exist yet. And there is no automatic way to call a method after a constructor.
One common way to handle this is to make the constructor(s) private so no instances can be created directly, and then write a separate factory that can create the instances. This way, every instance must be created by your factory, and the factory can do whatever it needs after calling a constructor before returning the new instance.
For example:
#include <unordered_map>
#include <functional>
#include <string>
#include <memory>
class A {
protected:
A() { ... }
public:
using A_ptr = std::unique_ptr<A>;
using createFunc = std::function<A_ptr()>;
virtual ~A() { ... }
virtual void fv() = 0;
void fx() { ... }
static void registerCreator(const std::string& name, createFunc creator) {
m_creators[name] = std::move(creator);
}
static A_ptr create(const std::string& name) {
auto instance = m_creators.at(name)();
instance->fv();
return instance;
}
private:
static std::unordered_map<std::string, createFunc> m_creators;
};
std::unordered_map<std::string, A::createFunc> A::m_creators;
class B : public A {
protected:
B() { ... }
public:
~B() { ... }
void fv() override { ... }
static A_ptr createMe() { return A_ptr(new B); }
};
class C : public B {
C() { ... }
public:
~C() { ... }
void fv() override { ... }
static A_ptr createMe() { return A_ptr(new C); }
};
int main(int argc, char **argv) {
A::registerCreator("B", &B::createMe);
A::registerCreator("C", &C::createMe);
auto b = A::create("B");
b->fx();
auto c = A::create("C");
c->fx();
}
Demo
6
You can achieve this by hiding the constructors and using a factory-like method to create objects.
The only way to create objects is to use the Factory
method since the B
constructor is private.
The Factory method creates the object and calls the fv
method.
Now, when you call the fx
method you are sure the fv
method has already been called.
For example:
class A{
protected:
A() {};
public:
virtual void fv() = 0;
void fx(){}
};
class B: public A
{
B():A(){};
public:
void fv() {};
static B Factory()
{
B obj;
obj.fv();
return obj;
}
};
int main(int argc, char **argv)
{
B b = B::Factory();
b.fx();
}
Note that you will have to use move and copy constructors for more complex objects of B
.