3

我会使用这样的单例:

Singleton* single = Singleton::instance();
single->do_it();

我会使用这样的未命名类:

single.do_it();

我觉得单例模式除了具有可读的错误消息外,似乎与未命名的类相比没有任何优势。使用单例比使用未命名的类对象更笨拙:首先,客户端必须首先获取实例的句柄;其次,实现者Singleton::instance()可能需要考虑并发性。

那么为什么以及如何选择单例而不是未命名的类呢?

作为附录,尽管未命名类的明显定义可能是

class {
    // ...
}single;

我也可以这样定义:

#ifndef NDEBUG
class Singleton__ {   // readable error messages,
#else
class {               // unnamed, clients can't instantiate
#endif
    // ...
}single;

后一种方法具有可读的编译器错误消息的优点,但在调试模式下不是单例。

4

8 回答 8

8

我认为最重要的原因是您不能将未命名的类放在命名空间范围内。因此,以下内容无效(gcc 接受,但警告。comeau 在严格模式下不接受):

class { } single;
int main() { }

的类型single没有链接,因为无法在另一个引用它的范围内声明它的名称(正是因为它没有名称)。但是使用它来声明single具有链接(此处外部)的 是无效的(3.5/8)。single必须在没有链接的 main 中本地定义。您也不能将单个传递给函数模板,并且它不能具有静态数据成员(因为无法定义它们)。所有这些限制使它或多或少地不适用于单例的替代品。

于 2008-12-28T03:10:33.020 回答
4

当然,在 C++ 中使用单例对象的主要原因是通过在实例方法中使用“延迟构造”来控制初始化顺序?

例如,我的大部分代码都使用了一个记录器单例,日志消息被写入其中。这在许多个月前作为一个很好的旧“全局”开始,但是在被尝试在构建之前使用它之后被咬了,现在它是一个单例:

前...

logger.write("Something bad happened..."); // crash if logger not constructed

...后

 Logger &getLogger()
 {
   static Logger logger_;
   return logger_;
 }

 getLogger().write("Something bad happened...");

我已经阅读了常规的“单身人士很糟糕”的帖子,但没有看到有人建议 C++ 的更好替代方案。

于 2008-12-28T09:45:11.303 回答
2

您可以在标头中声明并在 cxx 中实现的单例类,因此可以跨 cxx 文件共享。对于未命名的类,您不能这样做,因为每个 cxx 都会尝试拥有自己的对象实例。

于 2008-12-28T02:34:54.027 回答
1

像这样更改代码,即使它可能不应该影响代码生成也是一个糟糕的主意。迟早有人会做一个小的调整,在调试或发布中生成不同的代码,然后你会遇到无法在调试版本中复制的发布崩溃。

于 2008-12-28T02:11:05.060 回答
1

虽然很方便,但单身人士通常是个坏主意。有关替换设计,请参阅此页面

于 2008-12-28T09:20:15.193 回答
1

从您的问题中,我可以看出您并不真正了解单例模式的性质和目的。

如果您想让许多“客户”可以访问全局对象,并且您想确保只创建该对象的一个​​实例,则可以使用单例。以记录器对象为例。您希望能够从代码的任何部分进行记录,但您的项目中应该只有一个记录器。这是单身人士的理想场所。

您的示例看起来好像您创建了一个范围较小的本地对象。因为这个单例是不需要的。它使代码更清晰,更易于阅读。

于 2008-12-28T12:30:43.293 回答
0

如果(除了错误冗长)没有行为差异,则全局实例需要 1 个额外的 LOC,单例将需要一堆非平凡的样板。吻

于 2008-12-28T02:12:22.497 回答
0

使用单例。使用常量。在一个神类中初始化它们注意并避免静态初始化顺序惨败: http: //www.parashift.com/c++-faq-lite/ctors.html#faq-10.12

于 2008-12-28T16:41:28.437 回答