Singleton - 为什么要使用类?

时间:2009-09-08 13:46:36

标签: c++ singleton

就在前几天,我看到了使用所谓的单例模式的代码。

的含义
class MySingleton{
public:
    void foo() { ... }
    static MySingleton&get_instance(){
        static MySingleton singleton;
        return singleton
    }
private:
    MySingleton(){ ... }
    ~MySingleton(){ ... }
    int bar;
};

我确实知道为什么会这样做:

  • 使实例可以全局访问。
  • 确保该类的实例永远不会超过一个。

但是我不明白为什么这种做事方式优于一些免费功能。我实现它的方法是放

namespace some_name{
    void foo();
}
标题中的

namespace some_name{
    void foo(){
        ...
    }
}

在实现文件中。如果我需要初始化和/或清理,我要么添加一些必须显式调用的函数,要么添加

namespace{
    class Dummy{
        Dummy(){ ... }
        ~Dummy(){ ... }
    }dummy;
}

进入实施文件。

我知道这是从语义的角度来看单例,但是我看到第一个变体在C ++代码中使用得比第二个更常见。为什么?我认为第二个版本略胜一筹,所以我问自己是否遗漏了一些明显的东西。

  • 第二个版本实现起来更简单,更不容易出错。在第一个变体中,私有拷贝构造函数是为了证明这一点而缺少的。在第二个变体中,无法执行此错误。
  • 在第二个版本中,实现和界面更好地分开。在第一个中,必须在标题中声明所有私有成员。这样做的好处是,您可以从头开始重写实现,甚至不需要重新编译使用单例的任何内容。使用第一个变体时,即使只更改了轻微的实现细节,也很可能需要重新编译所有用户代码。
  • 两种情况都隐藏了实施细节。在使用私有的第一个变体中,在第二个变体中使用未命名的命名空间。

您能否解释一下为什么每个人都使用第一个变体?我没有看到在C语言中做旧事情的单一优势。

6 个答案:

答案 0 :(得分:7)

根据党的路线(E. Gamma,R。Helm,R。Johnson和J. Vlissides。Design Patterns: Elements of Reusable Object-Oriented Software。Addison-Wesley,Reading,MA,1995,p.128),单身人士提供与您提出的解决方案相比具有以下优势。

  • 您可以优化操作和表示,例如通过子类化。
  • 您可以稍后改变主意并拥有多个实例。
  • 您可以多态覆盖单例的方法。
  • 您可以通过使用所需的类初始化单例实例来在运行时配置应用程序。

话虽如此,在大多数情况下我认为额外的复杂性过多,很少使用我编写的代码中的模式。但是,当您设计其他人将使用的API时,我可以看到它的价值。

答案 1 :(得分:4)

这有帮助吗?

What is so bad about singletons? http://steve.yegge.googlepages.com/singleton-considered-stupid

重新说明:单身人士是一个美化的全球化,因此只需将其“实施”为全球性的。

答案 2 :(得分:3)

static MySingleton singleton;的构造在第一次使用时被调用。 (当调用get_instance()时。)如果从未调用它,它永远不会调用构造函数。您的方法将在静态构造时调用构造函数。前一种方法允许调用构造函数的顺序和时间。您的方法将根据编译器静态初始化顺序对每个单例的构造进行排序。

答案 3 :(得分:1)

要让函数+静态数据模拟单例模式,将依赖于C ++的文件范围和单独编译。这些是编译器构造而不是语言构造。单例类模式允许数据封装,而不管编译单元的位置如何;它被正确封装,即使它是在具有其他类和函数的文件中定义的。

此外,它实际上不会模拟单例模式的行为,而只是模拟静态对象的行为,这不是同一件事。单例的生命周期与包含它的过程的生命周期无关。正确形成的单例在第一次使用时被实例化,而静态数据在main()启动之前被实例化和初始化。这可能是一个问题,如果说对象的构造依赖于某些其他运行时实体的存在。此单例对象在实例化之前不占用任何内存(除了其静态实例指针)。它也可能在任何时候被破坏和重新创建,实际上很多次。

请注意,如果修改单例以使构造函数受保护而不是私有,则可以对其进行子类化(从而轻松应用重用单例模式),不能使用静态对象或具有所有静态成员的对象执行此操作,或者使用带有文件范围的静态数据的函数,或者您尝试正确执行此操作的任何其他方式。

您的建议本身没有任何问题,只要您知道它不是单身模式,并且缺乏灵活性。

答案 4 :(得分:0)

使用自由函数的问题在于,默认情况下这些函数不会获得共享持久行为,就像您的单例类可以使用成员变量和常量一样。

你可以继续使用静态全局变量来做同样的事情,但是对于想要弄明白的人来说,这使得他们必须看到的范围几乎无限地理解这些例程的行为。使用单例类,一切都很好地组织成一个类供读者检查。

答案 5 :(得分:0)

就个人而言,当我想控制第一次执行构造函数时,我会使用单例。

例如; 如果我的日志系统有单例,则打开文件进行写入的代码将被放入单例构造函数中,该函数称为like; Logger.instance();在我的启动过程中。 如果我使用命名空间,我将拥有该控制权。