有一些方法和属性是公共的,需要被全局使用,并且在整个应用程序的生存周期内存在,这时只需要维持有且仅有一个的类对象,重复地实例化是浪费内存且没有意义的。
复制粘贴是最容易的编程,也是最没价值的编程。
一、单例模式:
——保证一个类只有一个实例,并提供一个访问它的全局访问点。
通常我们可以定义全局变量使得一个对象被访问,但不能防止实例化多个对象。一个最好的办法是,让类自身负责保存它的唯一实例。这个类可以保证没有其它实例可以被创建,并且它可以提供一个访问该实例的方法。除此之外,将判断全局变量是否创建的责任交给单例类自身处理,减少了重复代码。
librecad中用到的单例类例子:
class RS_Settings {
public:
~RS_Settings();
static RS_Settings* instance();
//******************有省略********************************
private:
RS_Settings();
RS_Settings(RS_Settings const&) = delete;
RS_Settings& operator = (RS_Settings const&) = delete;
//******************有省略********************************
protected:
static RS_Settings* uniqueInstance;
//******************有省略********************************
};
RS_Settings* RS_Settings::uniqueInstance = nullptr;
RS_Settings* RS_Settings::instance() {
if (!uniqueInstance) {
uniqueInstance = new RS_Settings();
}
return uniqueInstance;
}
实用类:
和单例类相似,实用类也会采取私有化构造函数来避免该类存在实例,但是实用类不保存状态,仅仅提供一些静态方法或属性来让用户使用。单例类是有状态的,实用类不能用于继承多态,单例虽然实例唯一,确实可以有子类来继承。实用类不过是一些方法属性的集合,而单例却是有着唯一的对象实例。
librecad中用到的实用类例子:
class RS_Math {
private:
RS_Math() = delete;
public:
static int round(double v);
static double round(const double v, const double precision);
static double pow(double x, double y);
static RS_Vector pow(RS_Vector x, double y);
static bool equal(const double d1, const double d2);
//******************有省略********************************
};
二、多线程时的单例:
多个线程同时访问上述示例单例类时,调用instance()方法会有可能创建多个实例,这时可以通过给进程一把锁来处理,确保一个线程位于代码的临界区时,另一个线程不进入临界区,如果其它线程试图进入锁定的代码,则将一直等待,直到该对象被释放。
RS_Settings* RS_Settings::uniqueInstance = nullptr;
std::mutex RS_Settings::m_Mutex;//在h文件被声明为静态成员变量
RS_Settings* RS_Settings::instance() {
std::unique_lock<std::mutex> lock(m_Mutex);
if (!uniqueInstance) {
uniqueInstance = new RS_Settings();
}
return uniqueInstance;
}
这样每次调用instance方法都需要lock,会影响性能。改用双重锁定。
RS_Settings* RS_Settings::uniqueInstance = nullptr;
std::mutex RS_Settings::m_Mutex;//在h文件被声明为静态成员变量
RS_Settings* RS_Settings::instance() {
if (!uniqueInstance) {
std::unique_lock<std::mutex> lock(m_Mutex);
if (!uniqueInstance) {
uniqueInstance = new RS_Settings();
}
return uniqueInstance;
}
}
三、“饿汉式”单例类:
上述单例类为懒汉式,要在第一次引用时才将自己实例化。而使用静态初始化的方式在自己被加载的时候就将自己实例化,这样的方法称为“饿汉式”。
class RS_Settings {
public:
~RS_Settings();
static RS_Settings* instance();
//******************有省略********************************
private:
RS_Settings();
RS_Settings(RS_Settings const&) = delete;
RS_Settings& operator = (RS_Settings const&) = delete;
//******************有省略********************************
protected:
static RS_Settings* uniqueInstance;
//******************有省略********************************
};
RS_Settings* RS_Settings::uniqueInstance = new RS_Settings;
RS_Settings* RS_Settings::instance() {
return uniqueInstance;
}