动机
序列化库依赖于一些静态变量和表格的存在,用于存储与运行时类型相关的信息。例如,这些表格可以将导出的名称与类型相关联,或者将基类与派生类相关联。构建、销毁和使用这些变量需要考虑以下问题:
- 一些静态数据变量和常量条目相互引用。初始化的顺序不能是随意的,而必须是正确的顺序。
- 许多静态变量没有被显式引用,在没有特殊预防措施的情况下,它们将被大多数代码优化器删除。
- 这些变量中的许多是由模板创建的,必须特别小心确保它们被实例化。
- 在多线程系统中,这些静态变量可能会被单独的线程同时访问。这将导致竞争条件,行为不可预测。
这个单例类解决了上述所有问题。
特点
这个单例实现具有以下特点:
任何实例在尝试访问它之前都会被构建。
- 使用模板创建的任何实例都保证会被实例化。
- 无论实例是否被显式引用,当在发布模式下构建可执行文件时,不会被优化器删除。
- 所有实例都会在调用main函数之前被构建,无论它们在程序中的何处被引用。在多任务系统中,这保证在构建任何实例期间不会发生竞争条件。不需要线程锁定来保证这一点。
- 以上意味着在整个程序运行期间,任何const实例都是线程安全的。同样,不需要线程锁定。
- 如果创建了一个可变实例,并且在多线程系统中在调用main函数后修改了这样一个实例,则存在竞争条件的可能性。
- 序列化库确保在需要可变单例的少数情况下, 在调用main函数后不会更改它。对于更通用的用途,可以轻松地实现对这个单例的线程锁定。但是,由于序列化库不需要它,因此没有实现。
类接口
namespace boost {
namespace serialization {
template <typename T>
class singleton : public boost::noncopyable
{
public:
static const T & get_const_instance();
static T & get_mutable_instance();
static bool is_destroyed();
};
} // namespace serialization
} // namespace boost
-
static const T & get_const_instance();
:获取此类型的单例的常量引用。 -
static T & get_mutable_instance();
:获取此类型的单例的可变引用。 -
static bool is_destroyed();
:如果此单例的析构函数已被调用,则返回true。否则,返回false。
要求
为了将类型 T 用作 singleton<T>
,类型 T 必须具备默认构造能力。它不需要静态变量,尽管它可以拥有静态变量。由于库保证了只有一个 singleton<T>
实例,并且所有访问都通过上述静态接口函数进行,T 的普通成员函数成为等效于静态函数的功能性函数。
示例
有至少两种不同的使用此类模板的方式,这两种方式都在序列化库中被使用。
第一种方式由文件 extended_type_info.cpp 中的代码片段进行示例,其中包含以下代码:
typedef std::set ktmap;
...
void
extended_type_info::key_register(const char *key) {
...
result = singleton::get_mutable_instance().insert(this);
...
}
只要在程序中的任何地方引用了单例实例,就可以确保指定类型(例如,在此示例中的 ktmap)的一个且仅有一个实例在整个程序中存在。不需要任何其他的声明或定义。
第二种方式是将 singleton 用作类型的一个基类之一。以下是来自 extended_type_info_typeid.hpp 的简化代码示例:
template<class T>
class extended_type_info_typeid :
public detail::extended_type_info_typeid_0,
public singleton<extended_type_info_typeid<const T> >
{
friend class singleton<extended_type_info_typeid<const T> >;
private:
// 阻止存在除静态实例之外的任何构造函数的私有构造函数。注意:并非所有编译器都支持这一点!!!
extended_type_info_typeid() :
detail::extended_type_info_typeid_0()
{
type_register(typeid(T));
}
~extended_type_info_typeid(){}
...
};
这种用法允许更自然的语法:
extended_type_info_typeid<T>::get_const_instance()
同样,在程序的任何地方包含上述一个或多个语句都会确保创建并引用一个且仅有一个实例。