C++/python之设计模式(1)之什么是单例模式

注:整理一些突然学到的C++、python知识,随时mark一下
例如:忘记的关键字用法,新关键字,新数据结构



提示:本文为 C++、python 中单例模式的写法和举例


一、 单例模式

  单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类仅有一个实例,并提供一个全局性的访问点来获取该实例。单例模式在多种场景中都很有用,例如配置信息的读取、日志记录、线程池管理、数据库连接池等,这些场景通常只需要一个全局唯一的对象来执行特定的任务。

要实现单例模式,通常需要遵循以下步骤:

将构造函数私有化:通过将构造函数设为私有,可以阻止类的外部代码通过new关键字创建该类的实例。

在类内部创建静态实例:由于构造函数是私有的,类的外部无法创建实例,因此需要在类内部创建一个静态的实例。这个实例会在类首次被加载到内存时创建。

提供公共的静态方法获取实例:为了从类的外部获取这个唯一的实例,需要提供一个公共的静态方法,如getInstance(),用于返回这个静态实例。

禁止拷贝:为了防止通过拷贝构造函数和拷贝赋值操作符创建额外的实例,通常将它们设为私有或者禁用(使用= delete)。

1、C++单例模式例子

下面是一个简单的单例模式的C++实现示例:

cpp

#include <iostream>  
#include <memory>  
  
class Singleton {  
private:  
    // 私有构造函数,防止外部创建实例  
    Singleton() {  
        std::cout << "Singleton instance created." << std::endl;  
    }  
  
    // 禁用拷贝构造函数  
    Singleton(const Singleton&) = delete;  
  
    // 禁用拷贝赋值操作符  
    Singleton& operator=(const Singleton&) = delete;  
  
    // 静态成员变量,保存唯一实例  
    static std::unique_ptr<Singleton> instance;  
  
public:  
    // 静态方法,返回唯一实例  
    static Singleton* getInstance() {  
        if (!instance) {  
            instance.reset(new Singleton());  
        }  
        return instance.get();  
    }  
  
    // 其他成员函数...  
    void doSomething() {  
        std::cout << "Singleton doing something." << std::endl;  
    }  
};  
  
// 类外初始化静态成员变量  
std::unique_ptr<Singleton> Singleton::instance = nullptr;  
  
int main() {  
    Singleton* singleton1 = Singleton::getInstance();  
    Singleton* singleton2 = Singleton::getInstance();  
  
    // 检查是否指向同一个实例  
    if (singleton1 == singleton2) {  
        std::cout << "singleton1 and singleton2 are the same instance." << std::endl;  
    }  
  
    singleton1->doSomething();  
    return 0;  
}

在这个例子中,Singleton类通过以下方式确保单例行为:

构造函数是私有的,因此外部代码无法创建Singleton的实例。
静态成员变量instance是一个std::unique_ptr,它负责在程序结束时自动删除Singleton的唯一实例
getInstance()方法检查instance是否为空,如果是,则创建一个新的Singleton实例并返回其指针。由于instance是静态的,它只会被初始化一次,从而确保只有一个实例被创建。
拷贝构造函数和拷贝赋值操作符被禁用,防止了额外的实例被创建。
这个实现也考虑到了线程安全,因为在多线程环境下,可能会有多个线程同时调用getInstance()方法,从而导致创建多个实例。为了确保线程安全,通常需要使用互斥锁或其他同步机制来保护对instance的访问。在上面的示例中,由于使用了std::unique_ptr,在C++11及以后的版本中,其初始化是线程安全的,但getInstance()方法中的条件判断(如果实例为空则创建)并不是线程安全的,实际使用时需要添加适当的同步机制。

2、python单例模式例子

在Python中实现单例模式通常不需要像C++那样显式地控制构造函数和拷贝操作。Python提供了更简洁的方法来实现这一模式。下面是一个简单的Python单例模式的实现:

python

class Singleton:  
    _instance = None  
  
    def __new__(cls):  
        if not cls._instance:  
            cls._instance = super(Singleton, cls).__new__(cls)  
        return cls._instance  
  
    def do_something(self):  
        print("Singleton is doing something.")  
  
# 使用单例  
singleton1 = Singleton()  
singleton2 = Singleton()  
  
# 检查是否指向同一个实例  
if singleton1 is singleton2:  
    print("singleton1 and singleton2 are the same instance.")  
  
singleton1.do_something()

在Python中,我们使用__new__方法而不是__init__方法来控制实例的创建。__new__方法在__init__之前被调用,用于创建并返回实例对象。在上面的代码中,_instance是一个类变量,它保存了单例的唯一实例。在__new__方法中,我们检查_instance是否为None,如果是,则调用父类的__new__方法创建一个新的实例并将其赋值给_instance。随后,我们总是返回_instance,确保无论多少次调用Singleton(),都返回同一个实例。

请注意,Python的单例实现不需要显式地禁止拷贝,因为Python中的对象是通过引用传递的,而不是通过值传递。因此,赋值操作实际上只是复制引用,而不是复制对象本身。此外,Python也没有像C++那样的拷贝构造函数和拷贝赋值操作符需要特别处理。

另外,如果你需要线程安全的单例,Python的__new__方法通常也已经是线程安全的,因为在CPython解释器中,全局解释器锁(GIL)确保了同一时间只有一个线程可以执行Python字节码。然而,如果你使用的是多进程环境或者对性能有严格要求的场景,可能需要使用更复杂的线程同步机制。在大多数情况下,简单的单例实现就已经足够了。

在Python中,super()函数用于调用父类(超类)的一个方法。当子类需要重写父类的方法,但又想保留父类的一些功能时,可以使用super()来调用父类的方法。在单例模式的上下文中,使用super()是为了确保当我们创建单例类的实例时,仍然按照正常的对象创建流程调用父类(即object类,因为所有Python类最终都继承自object)的__new__方法。

super(Singleton, cls).new(cls)做了以下几件事情:

super(Singleton, cls)返回一个临时对象,这个对象绑定到Singleton类的父类(在这种情况下是object类),并接收Singleton类作为第一个参数和当前类(由cls变量表示)作为第二个参数。

.new(cls)调用object类的__new__方法,并传入当前类(cls)作为参数。这实际上是请求Python为cls类创建一个新的实例。

在单例模式中,使用super()的目的是确保Singleton类的新实例是通过正常的Python对象创建机制来创建的,然后再通过我们的单例逻辑(即检查_instance是否为None)来确保我们不会意外地创建多个实例。

总结

04-23 10:41