不得不说的创建型模式-单例模式

单例模式确保类只有一个实例并提供全局访问,常用于资源管理如数据库连接池。文中提供了C++实现单例模式的代码示例,包括饿汉式和懒汉式,以及线程安全的双检锁和静态局部变量实现。此外,还介绍了日志记录器、窗口管理器和缓存等应用场景。
摘要由CSDN通过智能技术生成

单例模式是创建型模式的一种,它的作用是确保一个类只有一个实例,并提供全局访问点。单例模式通常用于管理共享资源,如配置文件、数据库连接池等,它可以保证这些资源只被创建一次,并且可以被全局共享。

下面是一个使用C++实现单例模式的示例代码:

#include <iostream>
using namespace std;

class Singleton {
private:
    // 私有静态成员变量,用于保存唯一实例的指针
    static Singleton* instance;

    // 私有构造函数,防止外部创建新的实例
    Singleton() {
        cout << "创建单例对象" << endl;
    }

public:
    // 公有静态成员函数,用于获取唯一实例的指针
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    // 公有成员函数,用于测试单例对象的调用次数
    void test() {
        cout << "调用单例对象的test方法" << endl;
    }
};

// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;

int main() {
    // 获取单例对象
    Singleton* singleton1 = Singleton::getInstance();
    // 调用单例对象的test方法
    singleton1->test();

    // 获取单例对象
    Singleton* singleton2 = Singleton::getInstance();
    // 调用单例对象的test方法
    singleton2->test();

    // 判断singleton1和singleton2是否为同一个对象
    if (singleton1 == singleton2) {
        cout << "singleton1和singleton2是同一个对象" << endl;
    } else {
        cout << "singleton1和singleton2不是同一个对象" << endl;
    }

    return 0;
}

在上面的代码中,我们使用静态成员变量instance来保存唯一实例的指针,并使用静态成员函数getInstance来获取该指针。在getInstance方法中,我们判断instance是否为nullptr,如果是,则创建新的实例并赋值给instance;否则,直接返回instance。这样就可以保证整个程序中只有一个Singleton对象。

在实际应用中,单例模式可以用来管理各种共享资源,如数据库连接、文件系统、线程池等。在实现时,我们可以使用懒汉式单例模式,即在第一次调用getInstance方法时创建单例对象,以避免浪费系统资源。如果需要在多线程环境下使用单例模式,我们需要采用线程安全的实现方式,如双检锁单例模式或者使用C++11中的std::call_once函数。

单例模式是一种非常有用的设计模式,它可以确保一个类只有一个实例,并提供全局访问点。在实现时,我们需要注意线程安全性和懒加载性,以提高系统的性能和可靠性。

除了上述提到的资源管理外,单例模式还可以在许多其他场景中使用,例如日志记录器、窗口管理器、缓存等。下面分别介绍几个实际应用场景:

  1. 日志记录器

在大型软件系统中,日志记录是一项非常重要的工作。我们可以使用单例模式创建一个全局的日志记录器,以便在整个系统中都可以方便地访问和使用。这样做还可以避免由于多个日志记录器同时工作导致的日志信息不一致问题。

2.窗口管理器

在图形界面应用程序中,窗口管理器是一个重要的组件,它可以负责创建、显示和管理各种窗口。我们可以使用单例模式创建一个全局的窗口管理器,以便在整个系统中都可以方便地访问和使用。这样做还可以避免由于多个窗口管理器同时工作导致的窗口状态不一致问题。

3.缓存

在许多应用程序中,缓存是一项重要的技术,它可以减少系统的响应时间并提高性能。我们可以使用单例模式创建一个全局的缓存管理器,以便在整个系统中都可以方便地访问和使用。这样做还可以避免由于多个缓存管理器同时工作导致的缓存状态不一致问题。

总的来说,单例模式可以在许多不同的应用场景中使用,它可以确保一个类只有一个实例,并提供全局访问点。在实际应用中,我们需要根据具体的需求来设计和实现单例模式,以提高系统的性能和可靠性。

在实际编程中,实现单例模式的方式有多种,下面我们来看一种基于饿汉式的实现方法:

class Singleton {
private:
    static Singleton* instance;

    Singleton() {} // 将构造函数设置为私有,确保只能通过GetInstance()函数获取实例

public:
    static Singleton* GetInstance() {
        return instance;
    }

    void DoSomething() {
        // 实现单例模式的具体逻辑
    }
};

Singleton* Singleton::instance = new Singleton(); // 类的静态成员变量必须在类外进行初始化

int main() {
    Singleton* s1 = Singleton::GetInstance();
    Singleton* s2 = Singleton::GetInstance();
    assert(s1 == s2); // 验证s1和s2是否是同一个实例

    s1->DoSomething(); // 使用单例模式
    s2->DoSomething(); // 使用单例模式

    return 0;
}

在这个实现方法中,我们将类的构造函数设置为私有,这样外部就无法通过创建对象的方式来获取实例。然后我们定义了一个静态成员变量instance,它是一个指向Singleton对象的指针。在类的实现中,我们在静态成员变量instance上执行了一个new操作,来创建Singleton对象,并将其赋值给instance。

由于静态成员变量instance是一个全局变量,因此在程序启动时就会被创建,从而实现了饿汉式的单例模式。在GetInstance()函数中,我们只需要返回静态成员变量instance的值即可。这样,我们就可以通过Singleton::GetInstance()函数来获取Singleton的唯一实例了。

需要注意的是,饿汉式的单例模式在程序启动时就会被创建,因此会占用一定的内存空间。如果我们的应用程序需要创建多个单例模式,那么这种实现方法可能会导致内存浪费的问题。此时,我们可以考虑使用懒汉式的单例模式来避免这个问题。

除了饿汉式之外,懒汉式也是实现单例模式的一种常用方式。懒汉式的单例模式是在需要使用时才创建单例对象,避免了饿汉式的内存浪费问题。下面是一个基于懒汉式的单例模式的实现方法:

class Singleton {
private:
    static Singleton* instance;

    Singleton() {} // 将构造函数设置为私有,确保只能通过GetInstance()函数获取实例

public:
    static Singleton* GetInstance() {
        if (instance == nullptr) { // 如果instance为空,则创建Singleton对象
            instance = new Singleton();
        }
        return instance;
    }

    void DoSomething() {
        // 实现单例模式的具体逻辑
    }
};

Singleton* Singleton::instance = nullptr; // 类的静态成员变量必须在类外进行初始化

int main() {
    Singleton* s1 = Singleton::GetInstance();
    Singleton* s2 = Singleton::GetInstance();
    assert(s1 == s2); // 验证s1和s2是否是同一个实例

    s1->DoSomething(); // 使用单例模式
    s2->DoSomething(); // 使用单例模式

    return 0;
}

在这个实现方法中,我们同样将构造函数设置为私有,确保只能通过GetInstance()函数获取实例。与饿汉式不同的是,在GetInstance()函数中,我们首先判断静态成员变量instance是否为空。如果instance为空,则创建一个新的Singleton对象并将其赋值给instance。否则,直接返回instance的值即可。

懒汉式的单例模式具有延迟加载的特性,只有在需要使用时才会创建对象,避免了饿汉式的内存浪费问题。但需要注意的是,如果多个线程同时调用GetInstance()函数,可能会导致多个Singleton实例被创建。为了避免这个问题,我们可以使用双检锁机制或者静态局部变量等方式来保证线程安全。

双检锁机制是指在懒汉式的单例模式中,使用两个if语句来确保线程安全,从而避免多个线程同时创建多个实例的问题。下面是一个使用双检锁机制实现的懒汉式单例模式的例子:

class Singleton {
private:
    static Singleton* instance;
    static std::mutex mutex; // 用于实现线程安全

    Singleton() {} // 将构造函数设置为私有,确保只能通过GetInstance()函数获取实例

public:
    static Singleton* GetInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mutex); // 使用锁保证线程安全
            if (instance == nullptr) { // 双检锁机制
                instance = new Singleton();
            }
        }
        return instance;
    }

    void DoSomething() {
        // 实现单例模式的具体逻辑
    }
};

Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

int main() {
    Singleton* s1 = Singleton::GetInstance();
    Singleton* s2 = Singleton::GetInstance();
    assert(s1 == s2);

    s1->DoSomething();
    s2->DoSomething();

    return 0;
}

在这个实现方法中,我们使用了一个静态的互斥量mutex来保证GetInstance()函数的线程安全。当一个线程进入GetInstance()函数时,会先尝试获取互斥量mutex的锁,如果获取成功,则判断instance是否为空。如果instance为空,则创建一个新的Singleton对象并将其赋值给instance。在instance被创建后,释放互斥量mutex的锁,让其他线程可以继续访问GetInstance()函数。

这种实现方式可以避免多个线程同时创建多个实例的问题,同时也避免了饿汉式的内存浪费问题。不过需要注意的是,在使用互斥量mutex时需要考虑到性能问题,因为每次调用GetInstance()函数时都会涉及到锁的竞争和释放操作,可能会影响程序的性能。

除了双检锁机制之外,静态局部变量也是实现懒汉式单例模式的一种常用方式。静态局部变量的特点是在程序第一次进入这个函数时被初始化,避免了多线程竞争和锁的开销。下面是一个使用静态局部变量实现懒汉式单例模式的例子:

class Singleton {
private:
    Singleton() {} // 将构造函数设置为私有,确保只能通过GetInstance()函数获取实例

public:
    static Singleton* GetInstance() {
        static Singleton instance; // 静态局部变量
        return &instance;
    }

    void DoSomething() {
        // 实现单例模式的具体逻辑
    }
};

int main() {
    Singleton* s1 = Singleton::GetInstance();
    Singleton* s2 =Singleton::GetInstance();
    assert(s1 == s2);
    s1->DoSomething();
    s2->DoSomething();

    return 0;
}


在这个实现方法中,我们将Singleton对象作为静态局部变量在GetInstance()函数中定义,这样它会在程序第一次调用GetInstance()函数时被初始化。由于静态局部变量只会被初始化一次,所以不会出现多个线程同时创建多个实例的问题。

静态局部变量的实现方式比双检锁机制更加简单,不需要使用锁来保证线程安全,同时也避免了饿汉式的内存浪费问题。不过需要注意的是,在使用静态局部变量时需要考虑到线程安全的问题,因为静态局部变量只会被初始化一次,如果多个线程同时访问GetInstance()函数,可能会导致初始化顺序的问题,从而出现程序异常。

单例模式的实际应用非常广泛,例如在GUI程序中,需要保证只有一个主窗口,可以使用单例模式来实现;在多线程程序中,需要共享一个全局变量或对象,可以使用单例模式来实现;在数据库连接池中,需要保证只有一个数据库连接池对象,可以使用单例模式来实现。通过使用单例模式,我们可以方便地管理和控制全局唯一的对象,避免了重复创建对象的开销和资源浪费,提高了程序的性能和效率。
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五百五。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值