C++单例模式为何要实例化一个对象不全部使用static

 更新时间:2022年05月12日 09:01:37   作者:​ 编程学习网   ​  
这篇文章主要介绍了C++单例模式为何要实例化一个对象不全部使用static,文基于C++围绕主题展开详细内容,需要的小伙伴可以参考一下

C++的单例模式为什么不直接全部使用static,而是非要实例化一个对象?

通过getInstance()函数获取单例对象,这种模式的关键之处不是在于强迫你用函数来获取对象。关键之处是让static对象定义在函数内部,变成局部static变量。

看下这种实现方式的经典demo:

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton inst;
        return inst;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 其他数据函数
    // ...

private:
    Singleton() { ... }
    // 其他数据成员
    // ...
};

学名是:Meyers' Singleton。没错,也就是说这是Scott Meyers最早提出来的C++单例模式的推荐写法。

注意:这种单例写法需要C++11。因为是从C++11标准才开始规定 static变量是线程安全的。也就是说无需我们自己写加锁保护的代码,编译器能够帮我们做到。

所以C++程序员们不要在读完Java单例模式的资料之后,在C++程序中写double check或volatile了!

如果是把 static对象定义成 Singleton的私有static成员变量,然后getInstance()去返回这个成员即:

class Singleton {
public:
    static Singleton& getInstance() {
        return inst;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 其他数据函数
    // ...

private:
    Singleton() { ... }
    static Singleton inst;
    // 其他数据成员
    // ...
};
Singleton Singleton::inst;

虽然它也是 先getInstance()再访问,但这种不是Meyers' Singleton!

那么为什么Meyers推荐的是第一种的呢?

原因是这解决了一类重要问题,那就是static变量的初始化顺序的问题。

C++只能保证在同一个文件中声明的static变量的初始化顺序与其变量声明的顺序一致。但是不能保证不同的文件中的static变量的初始化顺序。

然后对于单例模式而言,不同的单例对象之间进行调用也是常见的场景。比如我有一个单例,存储了程序启动时加载的配置文件的内容。另外有一个单例,掌管着一个全局唯一的日志管理器。在日志管理初始化的时候,要通过配置文件的单例对象来获取到某个配置项,实现日志打印。

这时候两个单例在不同文件中各自实现,很有可能在日志管理器的单例使用配置文件单例的时候,配置文件的单例对象是没有被初始化的。这个未初始化可能产生的风险指的是C++变量的未初始化,而不是说配置文件未加载的之类业务逻辑上的未初始化导致的问题。

而Meyers' Singleton写法中,单例对象是第一次访问的时候(也就是第一次调用getInstance()函数的时候)才初始化的,但也是恰恰因为如此,因而能保证如果没有初始化,在该函数调用的时候,是能完成初始化的。所以先getInstance()再访问 这种形式的单例 其关键并不是在于这个形式。而是在于其内容,局部static变量能保证通过函数来获取static变量的时候,该函数返回的对象是肯定完成了初始化的!

讲到这,我们对Meyers' Singleton的盲目鼓吹也需冷静一下,因为C++同样能保证所有文件内(非函数内)的static变量在main()函数开始运行之后肯定是都能做完初始化的。所以如果你是在main()函数运行之后,用日志管理器的单例访问配置文件的单例,那么其实也是没有问题的… 这就引出Meyers' Singleton的第二个优势,那就是当产生继承的时候。

如果出现继承,这种写法中:

class Singleton {
public:
    static void on() {Singleton::isOn = true;}
    static void off() {Singleton::isOn = false;}
    static bool state() {return Singleton::isOn;}
private:
    static bool isOn;
};

class Monitor: public Singleton {
public:
    static void addBrightness(int val) { brightness += val;}
    static void subBrightness(int val) { brightness -= val;}
    static int getBrightness() { return brightness;}

private:
    static int brightness;
};

如果有子类继承这一父类,来拓展成新的子类,比如Monitor显示器类有开关状态,同时扩展了一个亮度的成员。但是父子类的static成员变量是共享的,其isOn成员会有问题。

好吧,如果你说你的单例完全不会出现继承的情况,是不是就不需要写成Meyers' Singleton?我只想说,如果你一定要强加这么多限定的话,那么这种设计模式的讨论本身就没有意义。就很像是在说:我自己能够保证每个new出来的指针我都能delete掉它,所以我不需要RAII……

总结:

所谓设计模式(design pattern)、惯用法(idiom)这种老程序员的经验之谈都是让你在大多数情况下,即使你不懂其奥秘,但凡遵守了,就能避免掉很多潜在的问题。尽管这种问题并不能百分百发生。所以这倒没必要去抬杠。

到此这篇关于C++单例模式为何要实例化一个对象不全部使用static的文章就介绍到这了,更多相关static的使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言实现动态顺序表的示例代码

    C语言实现动态顺序表的示例代码

    顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构。顺序表一般分为静态顺序表和动态顺序表,本文主要和大家介绍的是动态顺序表的实现,需要的可以参考一下
    2022-10-10
  • Qt编译OpenCV的实现步骤

    Qt编译OpenCV的实现步骤

    本文主要介绍了Qt编译OpenCV的实现步骤,通过详细的步骤和说明,帮助开发者在Qt环境中成功集成并编译OpenCV,从而为各类计算机视觉项目提供强大的支持,感兴趣的可以了解一下
    2024-01-01
  • C++编程之 std::forward使用例子

    C++编程之 std::forward使用例子

    std::forward 是一个 C++11 中的模板函数,其主要作用是在模板函数或模板类中,将一个参数以“原样”(forward)的方式转发给另一个函数,这篇文章主要介绍了C++编程之 std::forward,需要的朋友可以参考下
    2023-03-03
  • C语言main函数的三种形式实例详解

    C语言main函数的三种形式实例详解

    这篇文章主要介绍了 C语言main函数的三种形式实例详解的相关资料,需要的朋友可以参考下
    2017-06-06
  • C++面试八股文之位运算问题详解

    C++面试八股文之位运算问题详解

    这篇文章主要为大家介绍了C++面试八股文之位运算的问题解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • C语言动态内存管理介绍

    C语言动态内存管理介绍

    大家好,本篇文章主要讲的是C语言动态内存管理介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • Qt数据库相关应用开发总结

    Qt数据库相关应用开发总结

    这篇文章主要为大家介绍了在Qt数据库应用开发中的一些经验总结,以及一些组件的使用介绍。文中的示例代码讲解详细,需要的可以参考一下
    2022-02-02
  • Vscode Remote Development远程开发调试的实现思路

    Vscode Remote Development远程开发调试的实现思路

    这篇文章主要介绍了Vscode Remote Development远程开发调试的相关资料,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • C++中类的成员函数及内联函数使用及说明

    C++中类的成员函数及内联函数使用及说明

    这篇文章主要介绍了C++中类的成员函数及内联函数使用及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C语言简单实现快速排序

    C语言简单实现快速排序

    快速排序是一种不稳定排序,这篇文章主要为大家详细介绍了C语言简单实现快速排序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01

最新评论