摘要:如何在C ++中创建单例mixin?我试图避免复制相同的get_instance()
函数,私有构造函数等。但我无法找到一种方法使这个混合,因为静态实例将由从mixin继承的所有内容共享。
很容易使每个派生类成为单例,但有没有办法在不重复代码的情况下完成它?非常感谢你的帮助,我很难过。
代码:
我正在编写一个带有Registry
类的程序,用于按名称查找对象。
#include <string>
#include <memory>
#include <map>
#include <string>
#include <assert.h>
template <typename T>
class Registry
{
private:
// make private so that the class can't be instantiated and must be used via get_instance
Registry() {}
protected:
std::map<std::string, std::shared_ptr<T> > name_to_object_ptr;
public:
static Registry<T> & get_instance()
{
static Registry<T> instance;
return instance;
}
void register_name(const std::string & name, T*obj_ptr)
{
assert( name_to_object_ptr.count(name) == 0 );
name_to_object_ptr[name] = std::shared_ptr<T>(obj_ptr);
}
const std::shared_ptr<T> & lookup_name(const std::string & name)
{
assert( name_to_object_ptr.count(name) > 0 );
return name_to_object_ptr[name];
}
int size() const
{
return name_to_object_ptr.size();
}
};
我的Registry
班是一个单身人士;它必须是一个单例(这样注册的对象不会消失)。
class DerivedRegistryA : public Registry<int>
{
};
class DerivedRegistryB : public Registry<int>
{
};
int main()
{
DerivedRegistryA::get_instance().register_name(std::string("one"), new int(1));
std::cout << DerivedRegistryA::get_instance().size() << std::endl;
DerivedRegistryA::get_instance().register_name(std::string("two"), new int(2));
std::cout << DerivedRegistryA::get_instance().size() << std::endl;
DerivedRegistryA::get_instance().register_name(std::string("three"), new int(3));
std::cout << DerivedRegistryA::get_instance().size() << std::endl;
DerivedRegistryB::get_instance().register_name(std::string("four"), new int(4));
std::cout << DerivedRegistryB::get_instance().size() << std::endl;
return 0;
}
输出:
1
2
3
4
期望的输出:
1
2
3
1
答案 0 :(得分:4)
这不是混合物。您需要声明另一个模板参数并提供混合类。
template <typename T, typename Mixie>
class Registry
{
private:
Registry() {}
protected:
std::map<std::string, boost::shared_ptr<T> > name_to_object_ptr;
public:
static Registry<T,Mixie> & get_instance()
{
static Registry<T,Mixie> instance;
return instance;
}
...
};
class DerivedRegistryA : public Registry<int,DerivedRegistryA>
{
};
class DerivedRegistryB : public Registry<int,DerivedRegistryB>
{
};
答案 1 :(得分:3)
什么狮子说的听起来不错。这是一个与您的原始设计更相似的相关概念。
您声明一个模板类,其工作方式类似于Registry
个对象的工厂。我称之为RegAccess
:
template <typename RegType>
class RegAccess
{
public:
static RegType & get_instance()
{
static RegType instance;
return instance;
}
};
为了使它成功,你:
RegAccess<Registry<T> >
是Registry<T>
的朋友(为了能够做到这一点,您需要确保在 Registry<T>
Registry
的构造函数受保护,而不是私有(因此派生类的构造函数可以隐式使用它)get_instance
类定义Registry<T>
方法
然后您的主程序变为:
int main()
{
RegAccess<DerivedRegistryA>::get_instance().register_name(std::string("one"), new int(1));
std::cout << RegAccess<DerivedRegistryA>::get_instance().size() << std::endl;
RegAccess<DerivedRegistryA>::get_instance().register_name(std::string("two"), new int(2));
std::cout << RegAccess<DerivedRegistryA>::get_instance().size() << std::endl;
RegAccess<DerivedRegistryA>::get_instance().register_name(std::string("three"), new int(3));
std::cout << RegAccess<DerivedRegistryA>::get_instance().size() << std::endl;
RegAccess<DerivedRegistryB>::get_instance().register_name(std::string("four"), new int(4));
std::cout << RegAccess<DerivedRegistryB>::get_instance().size() << std::endl;
return 0;
}
当我测试它时,它产生了所需的输出。
答案 2 :(得分:2)
问题是DerivedRegistryA
和DerivedRegistryB
共享同一个name_to_object_ptr
get_instance
属于Registry<T>
,不属于任何DerivedRegistry
,DerivedRegistry
实际上属于Registry<int>
,例如两者都是同一类型。所以两者都共享相同的static
存储空间。因为静态存储属于不属于对象的类。
因此两者都获得了instance
Registry<T>
类型的同一副本。
您需要在派生类中具有实例函数,或者以某种方式将每个派生类视为不同的类型。有些方法可以通过在模板参数中进行更改来改变类型但不会改变逻辑。然而,这将是一个非常糟糕的设计。
您可以从get_instance
移除Registry
,而不是
template <typename T>
class Singleton{
public:
static T& get_instance(){
static T& instance;
return instance;
}
};
class DerivedRegistryA : public Registry<int>, public Singleton<DerivedRegistryA>{
};
这是一个通用解决方案,您可以将Singleton
类插入所有需要单例的类
答案 3 :(得分:2)
强制性说明: Aaarrg a Singleton:x (*)
有人说......
第1步:创建一个类型化的注册表。
template <typename T>
class Registry {
public:
typedef std::string Key;
typedef std::shared_ptr<T> ItemPtr;
Registry() {}
Registry(Registry const&) = delete;
Registry& operator=(Registry const&) = delete;
ItemPtr find(Key) const;
void insert(Key, ItemPtr);
private:
typedef std::map<Key, ItemPtr> StoreType;
StoreType _store;
}; // class Registry
第2步:创建标记的Singleton实现。
template <typename Object, typename>
class Singleton {
public:
static Object& GetMutableInstance() {
static Object O;
return O;
}
static Object const& GetInstance() { return GetMutableInstance(); }
private:
Singleton() = delete;
}; // class Singleton
注意:标签(第二个参数)在实现本身中完全没用。它只是一种允许为类似类型的对象创建不同实例(以及单个单例)的方法。可以提供默认值。
第3步:享受。
struct TypeA {};
typedef Singleton<Registry<int>, TypeA> RegistryA;
int main() {
RegistryA::GetMutableInstance().insert("toto", std::make_shared(3));
}
(*)为什么选择单身人士?
老实说,大多数时候单身人士都没用。哦,它确实使出现更容易。一开始。
Singleton单手设法累积全局变量的弱点(多线程问题,测试问题等);并且通过其独特性强制执行(更多测试问题)以及通常更详细的界面(GetInstance
地狱)来增加另一层蹩脚。
你应该至少摆脱执行的唯一性,只提供一个常规的全局变量(昨天是 Velociraptor Awareness Day ,要注意他们可能仍然潜伏着。)
当然,最好的道路就是只有一个常规对象,并通过引用那些需要它的函数/方法/对象来传递它。更容易跟踪,更容易测试。
答案 4 :(得分:2)
这应该有助于解决您的问题。请注意,此单例不是线程安全的。但是如果需要,你可以改变它。
有关详细信息,请参阅CRTP。
#include <iostream>
#include <map>
#include <string>
// simple singleton
template <class T>
class Singleton {
public:
static T& Instance() { static T instance; return instance; }
protected:
Singleton(){}
};
// your Registry Base
template <class T>
class Registry : public Singleton< Registry<T> > {
friend class Singleton<Registry>;
public:
void register_name( const std::string& name, T value ){ m_data[name] = value; }
const T& lookup_name( const std::string& name ){ return m_data[name]; }
private:
Registry(){}
Registry(const Registry&){} // to prevent copies, you have to use ::Instance()
std::map<std::string, T> m_data;
};
int main(int argc, char *argv[])
{
Registry<int>& instance = Registry<int>::Instance();
instance.register_name("Value1",1);
Registry<int>::Instance().register_name("Value2",2);
int value = instance.lookup_name("Value1");
std::cout << "Value1=" << value << std::endl;
std::cout << "Value2=" << Registry<int>::Instance().lookup_name("Value2") << std::endl;
return 0;
}