0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

为什么C++单例模式不能直接全部使用static变量和static函数呢?

Linux爱好者 来源:Linux爱好者 作者:Linux爱好者 2022-06-05 14:14 次阅读

开场

前段时间在知乎回答了这样一个问题:

为什么C++单例模式不能直接全部使用 static变量和 static函数呢?如果全部使用 static的话,是不是也不会有多线程的问题了?而且“类型::方法”的访问方式比起先getInstance()再访问难道不是更加简单清晰吗?

(还是说是为了附和 “单例” 这样一个字面上的意思)

//大概这个样子
classSingleton{
public:
staticvoidon(){Singleton::isOn=true;}
staticvoidoff(){Singleton::isOn=false;}
staticboolstate(){returnSingleton::isOn;}
private:
staticboolisOn;
};

这可能是很多C++学习者都会有的疑惑,下面是我的回答。

正文

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

classSingleton{
public:
staticSingleton&getInstance(){
staticSingletoninst;
returninst;
}
Singleton(constSingleton&)=delete;
Singleton&operator=(constSingleton&)=delete;

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

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

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

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

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

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

classSingleton{
public:
staticSingleton&getInstance(){
returninst;
}
Singleton(constSingleton&)=delete;
Singleton&operator=(constSingleton&)=delete;

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

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

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


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

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

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

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

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

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

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

classSingleton{
public:
staticvoidon(){Singleton::isOn=true;}
staticvoidoff(){Singleton::isOn=false;}
staticboolstate(){returnSingleton::isOn;}
private:
staticboolisOn;
};

classMonitor:publicSingleton{
public:
staticvoidaddBrightness(intval){brightness+=val;}
staticvoidsubBrightness(intval){brightness-=val;}
staticintgetBrightness(){returnbrightness;}

private:
staticintbrightness;
};

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

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

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

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    3868

    浏览量

    61308
  • C++
    C++
    +关注

    关注

    21

    文章

    2066

    浏览量

    72899

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

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    要怎么增加FLEXRAY STATIC MESSAGE ?

    我使用 SDL 7.8.0 版本,芯片是 tviibh8m 我看了 SDL 里面的 FLEXRAY 示例 comm_static_dynamic 我想要增加 STATIC MESSAGE
    发表于 02-01 08:33

    如何在嵌入式C编码中规范变量

    ◎ 不用或者少用全局变量 说明:单个文件内部可以使用static的全局变量,可以将其理解为类的私有成员变量。全局变量应该是模块的私有数据,
    的头像 发表于 12-07 14:42 140次阅读

    static关键字的三种用法

    (StaticVariables):在C语言中,使用`static`关键字来声明静态变量是一种常见的用法,它具有以下主要作用:1.生存期延长:静态变量的生存期在程序的整个运行期间,而不仅仅是在声明它的
    的头像 发表于 11-10 08:00 249次阅读
    <b class='flag-5'>static</b>关键字的三种用法

    Java中对static关键词的介绍

    static 是Java的一个关键字,可以用来修饰成员变量、修饰成员方法、构造静态代码块、实现静态导包以及实现静态内部类,下面我们来分别介绍。 1、修饰成员变量static 修饰
    的头像 发表于 10-11 15:26 253次阅读
    Java中对<b class='flag-5'>static</b>关键词的介绍

    static定义内部类

    1. static定义内部类 1.1 static定义内部类 如果说现在内部类上使用了static定义,那么这个内部类就变成了“外部类”,static定义的都是独立于类的结构,所以该类
    的头像 发表于 10-10 16:08 302次阅读

    深入理解C++static”关键字

    编译后的程序存储在硬盘上,准备执行时操作系统需要将它们读取到 RAM 中,这个时期就叫加载期。.data/.rodata 段的数据就是在这一时期分配内存的,一个常见的误区就是认为 static 数据是处于编译期。
    的头像 发表于 08-14 12:25 465次阅读
    深入理解<b class='flag-5'>C++</b> “<b class='flag-5'>static</b>”关键字

    UVM中通过静态类实现对全局资源实现管理

    Systemverilog中可以使用static修饰变量,方法,得到静态变量和静态函数static也可以
    的头像 发表于 08-07 17:35 1093次阅读
    UVM中通过静态类实现对全局资源实现管理

    【知识科普】C语言的static究竟限制了谁

    中,无明确说明时,指的都是static修饰函数,而非变量。 1 问题来源 最近的几个月一直在维护我的个人专属【嵌入式技术交流群】,平时大家都很积极在群里讨论技术问题。 就在上个星期,在群里收到一个有关
    的头像 发表于 08-02 20:05 441次阅读
    【知识科普】C语言的<b class='flag-5'>static</b>究竟限制了谁

    C语言的static究竟限制了谁?介绍一种绕开static限制的方法

    大佬们,问一个比较低智问题,static修饰的变量不是只能被当前文件所调用吗?为什么这里在其他.c文件可以调用get_board_led_opr这个函数获取board_demo_led_opr这个
    的头像 发表于 07-28 15:10 697次阅读
    C语言的<b class='flag-5'>static</b>究竟限制了谁?介绍一种绕开<b class='flag-5'>static</b>限制的方法

    LL库中常见关键字__STATIC_INLINE

    LL库中常见关键字__STATIC_INLINE,其定义见cmsis_gcc.h
    的头像 发表于 07-24 11:30 836次阅读
    LL库中常见关键字__<b class='flag-5'>STATIC</b>_INLINE

    嵌入式-C++函数的重载

    一、什么是函数的重载 两个以上的函数,具有相同的函数名,通过参数的类型和参数的个数不同。编译器自行匹配,自动确定调用哪一个函数 二、函数重载
    发表于 06-28 13:54

    static的全局变量与局部变量的使用,看完你就懂了

    1. 什么是static?staticC/C++ 中很常用的修饰符,它被用来控制变量的存储方式和可见性。 1.1
    发表于 06-27 08:54

    【笔记】static 在C语言中的用法

    当`static`关键字用于不同的上下文时,其含义和作用也会有所不同。下面是更多示例代码,展示了`static`在不同用法下的具体效果:示例1:静态局部变量#includevoidincrement
    的头像 发表于 06-12 10:03 682次阅读
    【笔记】<b class='flag-5'>static</b> 在C语言中的用法

    如何为xtensa编译C++

    我想为 xtensa 编译简单的 C++ 代码,但我不能。 代码:全选#include \"ets_sys.h\" #include \"osapi.h\"
    发表于 06-09 07:02

    JAVA语言中Static关键字使用

    注:为解决安卓开发中服务器IP随机变化导致客户端找不到服务器的问题,通过关键字Static修饰成员变量User_url,通过客户端主函数代码获取输入的服务器IP值赋值给User_url,然后其它Activity即可使用该静态
    的头像 发表于 06-04 16:45 334次阅读
    JAVA语言中<b class='flag-5'>Static</b>关键字使用