Reputation: 29
I am trying to implement an easy inherited Singleton pattern for my child classes. Therefore, I am implementing a parent class and, as I want to make things as easy as possible for others to create a new Singleton child class, I need to handle inside the father class constructor all the operations needed for the Singleton implementation for the child classes.
#include <vector>
#include <typeinfo>
#include <iostream>
class Father
{
protected:
Father()
{
for(auto & instance : Father::singletonInstances)
if(typeid(instance) == typeid(this)) // typeid(this) will always be "Father", which is actually the issue
{
// Singleton instance already exists for this class
* this = instance;
std::cout<<"An instance of the given class is already active\n";
return;
}
std::cout<<"Constructed\n";
// Otherwise, mark this as the Singleton instance for this class
Father::singletonInstances.emplace_back(this);
}
public:
Father operator=(Father * inputObj) { return * inputObj; }
private:
static std::vector<Father *> singletonInstances;
};
std::vector<Father *> Father::singletonInstances;
class Child : protected Father
{
public:
Child() : Father() {}
};
class Child2 : protected Father
{
public:
Child2() : Father() {}
};
int main()
{
new Child();
new Child2();
return 0;
}
Output :
Constructed
An instance of the given class is already active
So, to make things clear again : - The issue is that typeid(this) is always "Father" inside the constructor - new Child(); new Child2(); should be allowed - new Child(); new Child(); should not be allowed - No modifications should be made to the child classes
I know my implementation of the Singleton could look rather strange. I am open to new ideas.
I was able to implement these ideas in JScript, yet in C++ I cannot seem to find a way to make it work.
Upvotes: 0
Views: 2601
Reputation: 12978
When a class with inheritance is created the base class constructor is called first, after that the child classes upward in the hierarchy. This means that in the constructor of Father
, Child/Child2
is not yet constructed.
Trying to use the object before it's constructed would probably result in Undefined Behavior. C++ tries to protect you from this. Read for example the FAQ Lite. This is not the exact same thing, but related and reading this should give you an idea why typeid
would say that the object is a Father
.
On cppreference we can read
If typeid is used on an object under construction or destruction (in a destructor or in a constructor, including constructor's initializer list or default member initializers), then the std::type_info object referred to by this typeid represents the class that is being constructed or destroyed even if it is not the most-derived class.
I have seen several ways to implement singletons, the most modern seems to be to return a reference to a static function object. Here is one idea.
#include <iostream>
template <typename T>
T& Singleton() {
static T single;
return single;
}
class Foo {
private:
Foo() {
std::cout << "Constructed" << std::endl;
}
friend Foo& Singleton<Foo>();
public:
void print() {
std::cout << "Foo" << std::endl;
}
};
int main() {
//Foo f; not allowed
Singleton<Foo>().print();
Singleton<Foo>().print();
}
This requires every singleton instance to have a private constructor and to friend
the template instantiation. Only the Singleton
function will be allowed to construct a Foo
and it will only construct 1 copy ever. It's also thread safe and you don't need to worry about clean-up. Other approaches can be found if you google around.
Upvotes: 4