单例很糟糕?

之前专门整理过 C++ 怎么写单例类,平日项目中涉及的场景大多简单,使用最基本的实现就完全能够应付过来。但是,还是有些复杂的场景需要考虑。

相互引用的单例们

也就是 singletonA、singletonB 两者的构造相互调用。

使用常用的局部静态变量方式(lazy_result_loop.cpp),也是懒汉模式一种:相互依赖初始化,死循环,阻塞

1
2
Object_2B_1::Object_2B_1() this:[00BFC14C] data_2b_1_ [1].
Object_2B_2::Object_2B_2() this:[00BFC154] data_2b_2_ [2].

使用全局静态变量方式(hungry_result_different.cpp),饿汗模式:不会死循环,但奇葩在 Debug、Release 模式执行结果不一致

代码摘抄自 BOOST 的 Singleton 模版详解,以上就是“2B 实现有问题”。虽然作者自述“BOOST 的实现如何规避问题”,但在 msvc2015 中实测其代码并未解决问题,依旧是阻塞结果。

参考 boost 中的单例模式,boost 的实现解决的只是多线程问题(新标准才保证创建局部静态变量时的线程安全):

这个单例是利用 main 调用之前,程序只有一个线程,来保证单例在多线程下的唯一性。

但为什么要多此一举引入局部类呢?直接用全局静态变量不行吗?

线程安全

使用静态变量方式实现(无论是局部的还是全局的)都能够避免:

多线程调用 Singleton 导致多次初始化的问题

boost 中的单例模式 中给出的四种实现,因为要兼容旧标准,所以有三种都是饿汗模式。

boost 没有一个通用的单例类的原因,已提供的单例类,都是在某个具体的模块中。

单例很糟糕?

What is so bad about singletons?

C++ 他爹都说要避免使用单例,因为单例本质上是复杂化的全局变量,而 非常量的全局变量也是要避免的

结合我在 PC 客户端数据层的开发经历来说,自带线程管理的生产者、消费者、日志管理类、监控对象等(尤其是在 DLL 中使用时),大大增加了问题的复杂度,引入冲突,甚至引入 BUG。但解决方案是什么?更好的编码模式是什么?