假设我们需要在项目中只有一个类的一个实例。有几种方法可以做到。
我想比较一下。请你复习我的理解。
1)古典单身人士模式
2)完全静态的类(所有方法和成员都是静态的)。
据我所知,差异如下:
a)未定义跨不同单元的静态成员的初始化顺序。 因此,完全静态成员初始化不能使用来自其他模块的任何静态成员/函数。单身人士没有这个问题。
b)我们必须处理Singleton的getInstance()的线程。但是,完全静态类没有这个问题。
c)访问方法看起来有点不同。 FOO ::巴(); vs Foo :: getInstance() - > bar(); 通常,单例可以返回NULL以识别构造对象时存在一些问题,而静态类则不能。
d)对于静态类的一堆静态,类的定义看起来有点笨拙。
我错过了什么吗?
答案 0 :(得分:20)
无论你称它为Singleton还是Monostate还是任何花哨的名字......这个令人烦恼的本质就是你有一个对象的实例和许多写入它:全局变量,无论它们是什么样的,都是邪恶强>
您需要一个独特实例的想法通常很笨拙。大多数时候,您真正需要的是通信共享相同实例的部分。但是另一组部件可以完美地使用另一个实例而没有问题。
声称需要全局变量的任何代码都非常可疑。使用它可能看起来更简单,但让我们面对它,你可以完美地将对象传递给每个函数,它会使它们的签名变得复杂但它仍然可以工作。
但是,我承认,在您发现问题之前,使用全局变量似乎更简单:
现在,就单例而言,多线程创建在C ++ 0x之前的C ++中是不可用的(当它变得可能使用静态本地时),因此你需要在一个线程中创建它并延迟访问:instantiate它是主要的,这是你最好的选择。
破坏可能会导致混乱,因为Singleton / Static的生命可能会在其他人完成之前结束,然后是未定义的行为。这是典型的Logger
单身人士。通常的策略是无耻地泄漏......
在那之后,如果你还想要一个,我祝你好运,这就是所有社区都可以为你做的。
答案 1 :(得分:10)
您忽略的另一个选项是namespace
。
namespace xyz {
namespace {
int private_variable;
}
int get_pv() {
return private_variable;
}
}
从功能上讲,这与你的选项#2类似,但你不能不小心“删除”这个东西。您不能意外地创建它的实例。它只是一组相关的全球可访问数据和功能。你可以(如我的例子中)甚至拥有“私人”成员和职能。
当然使用方式如下:
int x = xyz::get_pv();
答案 2 :(得分:1)
假设我们需要在项目中只有一个类的一个实例。有几种方法可以做到。
作为参数传递给所有必需函数的main中的变量将是另一个变量。
a)未定义跨不同单元的静态成员的初始化顺序。因此,完全静态成员初始化不能使用来自其他模块的任何静态成员/函数。单身人士没有这个问题。
如果单身人士的构造函数/析构函数访问其他全局静态生命周期变量,则会遇到此问题。
b)我们必须处理Sigleton的getInstance()的线程。但是,完全静态类没有这个问题。
不是真的有问题吗?如果您知道它,只需在代码中添加适当的锁。
c)访问方法看起来有点不同。 FOO ::巴(); vs Foo :: getInstance() - > bar();通常,sigleton可以返回NULL以识别构造对象时存在一些问题而静态类不能。
我会让我的getInstance()返回一个引用。如果指针为NULL,则没有歧义。它要么工作,要么抛出异常。这也导致了一个设计,其中在实例上调用了正确的破坏(不要把它作为使用Singleton的建议,如果可能的话,我会避免使用它(但如果你确实使用它使它整洁)。
d)对于静态类的一堆静态,类的定义看起来有点笨拙。
没有比正确编写单身人士更笨拙。
这两种方法的问题在于它们都访问global mutable state
,因此其他对象对这些“单实例”对象的使用对用户是隐藏的。这可能导致测试问题(TDD需要模拟外部功能,但global mutable state
会阻止测试人员模拟外部依赖(轻松))。
任何非POD的对象都有一个可能会抛出异常的构造函数。因此,对于全局命名空间中的对象,这意味着在输入main()之前可以抛出异常(这可能导致很难找到错误(如果你有很多全局对象(你必须在任何地方放置断点)。)但是对于懒惰评估的单例存在同样的问题;如果在第一次使用它时会抛出如何纠正这一点,以便后续尝试不抛出?你的应用程序会在每次检索单例时继续抛出吗?
答案 3 :(得分:1)
您也可以使用Borg模式,除非您可以访问共享指针类,否则它在C ++中会稍微复杂一些。这个想法是可以实例化任意数量的Borg类,但它们的状态在所有实例中共享。
答案 4 :(得分:0)
您可以添加:静态对象可以抛出异常。可执行文件不会启动,很难调试/处理。