我正在尝试为我的子类实现一个简单的继承Singleton模式。 因此,我正在实现一个父类,因为我想让其他人尽可能容易地创建一个新的Singleton子类,我需要在父类构造函数内部处理Singleton实现所需的所有操作类。
#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;
}
输出:
Constructed
An instance of the given class is already active
所以,再说一遍: - 问题是typeid(this)总是&#34;父亲&#34;在构造函数内部 - 新孩子(); new Child2();应该被允许 - 新孩子();新孩子();不应该被允许 - 不应对子类进行任何修改
我知道我对Singleton的实现看起来很奇怪。我对新想法持开放态度。
我能够在JScript中实现这些想法,但在C ++中,我似乎无法找到使其工作的方法。
答案 0 :(得分:4)
当创建具有继承的类时,首先调用基类构造函数,然后在子层次结构中向上调用子类。这意味着在Father
的构造函数中,Child/Child2
尚未构建。
在构造对象之前尝试使用该对象可能会导致未定义的行为。 C ++试图保护您免受此攻击。阅读例如the FAQ Lite。这不是完全相同的事情,但相关和阅读此内容可以让您了解为什么typeid
会说对象是Father
。
在cppreference我们可以阅读
如果在构造或销毁的对象上使用typeid(在析构函数或构造函数中,包括构造函数的初始化列表或默认成员初始值设定项),则此typeid引用的std :: type_info对象表示的类是正在建造或销毁,即使它不是最衍生的阶级。
我已经看到了几种实现单例的方法,最现代的似乎是返回对静态函数对象的引用。这是一个想法。
#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();
}
这要求每个单例实例都有一个私有构造函数,并要求friend
模板实例化。只允许Singleton
函数构造Foo
,它只会构造1个副本。它也是线程安全的,你不需要担心清理。如果你谷歌可以找到其他方法。