最近,我遇到了C++的Singleton设计模式的实现/实现。它看起来是这样的(我采用了现实生活中的例子):
// a lot of methods are omitted here
class Singleton
{
public:
static Singleton* getInstance( );
~Singleton( );
private:
Singleton( );
static Singleton* instance;
};
从这个声明中,我可以推断实例字段是在堆上启动的。这意味着存在内存分配。对我来说完全不清楚的是,内存将在何时被释放?或者是否存在错误和内存泄漏?似乎在实现方面存在问题。
我的主要问题是,如何以正确的方式实现它?
除了这里的其他讨论之外,可能值得注意的是,您可以使用全局性,而不限于一个实例。例如,考虑引用计数的情况。。。
struct Store{
std::array<Something, 1024> data;
size_t get(size_t idx){ /* ... */ }
void incr_ref(size_t idx){ /* ... */}
void decr_ref(size_t idx){ /* ... */}
};
template<Store* store_p>
struct ItemRef{
size_t idx;
auto get(){ return store_p->get(idx); };
ItemRef() { store_p->incr_ref(idx); };
~ItemRef() { store_p->decr_ref(idx); };
};
Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances
现在,在函数(如main)中的某个位置,您可以执行以下操作:
auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201);
ref不需要将指针存储回各自的store,因为这些信息是在编译时提供的。您也不必担心Store的生存期,因为编译器要求它是全局的。如果确实只有一个Store实例,那么这种方法没有开销;在不止一个实例的情况下,编译器需要对代码生成进行巧妙处理。如果需要,ItemRef类甚至可以成为Store的朋友(您可以有模板朋友!)。
如果Store本身是一个模板化的类,那么事情就变得更糟了,但是仍然可以使用这个方法,也许可以通过实现具有以下签名的helper类:
template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning
instances of ItemRef<Store_t, store_p>. */ };
用户现在可以为每个全局Store实例创建StoreWrapper类型(和全局实例),并始终通过其包装器实例访问商店(从而忘记使用Store所需的模板参数的详细信息)。
2008年,我提供了一个Singleton设计模式的C++98实现,该模式是惰性评估的、保证破坏的,在技术上不是线程安全的:有人能给我提供一个c++中的Singleton示例吗?
这里是Singleton设计模式的一个更新的C++11实现,它是延迟评估的、正确销毁的和线程安全的。
class S
{
public:
static S& getInstance()
{
static S instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return instance;
}
private:
S() {} // Constructor? (the {} brackets) are needed here.
// C++ 03
// ========
// Don't forget to declare these two. You want to make sure they
// are inaccessible(especially from outside), otherwise, you may accidentally get copies of
// your singleton appearing.
S(S const&); // Don't Implement
void operator=(S const&); // Don't implement
// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
public:
S(S const&) = delete;
void operator=(S const&) = delete;
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
};
请参阅本文,了解何时使用单例:(不经常)辛格尔顿:应该如何使用
请参阅这两篇关于初始化顺序和如何处理的文章:静态变量初始化顺序查找C++静态初始化顺序问题
请参阅这篇描述寿命的文章:C++函数中静态变量的生存期是多少?
请参阅本文,其中讨论了单线程的一些线程含义:Singleton实例声明为GetInstance方法的静态变量,它是线程安全的吗?
请参阅这篇文章,解释为什么双重检查锁定在C++上不起作用:C++程序员应该知道哪些常见的未定义行为?Dobbs博士:C++和双重检查锁定的危险:第一部分