1、懒汉式与饿汉式单例模式介绍:
1.1 懒汉式单例模式:
懒汉式单例模式指的是在第一次访问时才创建唯一实例。这种实现方式在实例创建开销较大或者实例使用不频繁时,可以减少不必要的资源开销。但在多线程环境下,需要使用同步锁来确保线程安全。
1.2 饿汉式单例模式:
饿汉式单例模式指的是在类加载时就创建唯一实例。这种实现方式能保证线程安全,因为类加载时的操作是线程安全的。但是,由于实例在类加载时就创建,无论是否需要使用都会占用资源,可能导致资源浪费。
2、单例模式的适用场景:
- 当类的创建成本较高或实例化过程复杂时,适合使用单例模式。
- 当一个类需要在整个系统中提供唯一的全局访问点,如配置管理、数据库连接池等。
- 当需要频繁访问且无需多个实例的对象,如缓存、日志等。
3、单例模式的优点:
- 节省资源,只创建一个实例,避免了重复创建相同对象。
- 提供全局访问点,便于在系统中访问单例对象。
- 可以控制客户端对实例的访问,可以进行访问控制和资源共享。
4、单例模式的缺点:
- 单例模式扩展困难,不易继承和修改。
- 对测试不友好,因为无法为测试创建多个独立的实例。
- 可能导致对资源的过度共享,导致资源竞争问题。
5、用C++语言实现懒汉式线程安全的单例模式:
#include <iostream>
#include <mutex>
class SingletonLazySafe {
private:
static SingletonLazySafe* instance;
static std::mutex mtx;
SingletonLazySafe() {}
public:
SingletonLazySafe(const SingletonLazySafe&) = delete;
SingletonLazySafe& operator=(const SingletonLazySafe&) = delete;
static SingletonLazySafe* getInstance() {
if (instance == nullptr) {
std::unique_lock<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new SingletonLazySafe();
}
}
return instance;
}
};
SingletonLazySafe* SingletonLazySafe::instance = nullptr;
std::mutex SingletonLazySafe::mtx;
int main() {
SingletonLazySafe* instance1 = SingletonLazySafe::getInstance();
SingletonLazySafe* instance2 = SingletonLazySafe::getInstance();
std::cout << instance1 << std::endl;
std::cout << instance2 << std::endl;
return 0;
}
在这个C++实现中,我们首先定义一个 SingletonLazySafe 类。类中有一个私有静态指针 instance 用于保存唯一的对象实例,同时有一个私有静态互斥锁 mtx 用于确保线程安全。构造函数是私有的,以防止外部创建新实例。
我们禁用了拷贝构造函数和赋值操作符,以确保实例不会被复制。然后,我们定义了一个公共静态函数 getInstance() 用于获取单例实例。在 getInstance() 函数中,我们首先检查 instance 是否为空,如果为空,则获取互斥锁并再次检查。如果仍为空,则创建一个新的 SingletonLazySafe 实例。
在 main 函数中,我们调用getInstance() 两次并将结果存储在两个指针变量 instance1 和 instance2 中。然后我们将两个指针的内存地址打印到控制台。由于这是一个单例模式的实现,所以这两个指针指向相同的内存地址,证明只创建了一个实例。
6、用C++语言实现饿汉式的单例模式:
#include <iostream>
class SingletonEagerSafe {
private:
static SingletonEagerSafe instance;
SingletonEagerSafe() {}
public:
SingletonEagerSafe(const SingletonEagerSafe&) = delete;
SingletonEagerSafe& operator=(const SingletonEagerSafe&) = delete;
static SingletonEagerSafe* getInstance() {
return &instance;
}
};
SingletonEagerSafe SingletonEagerSafe::instance;
int main() {
SingletonLazySafe* lazyInstance1 = SingletonLazySafe::getInstance();
SingletonLazySafe* lazyInstance2 = SingletonLazySafe::getInstance();
std::cout << "Lazy instances: " << lazyInstance1 << " " << lazyInstance2 << std::endl;
SingletonEagerSafe* eagerInstance1 = SingletonEagerSafe::getInstance();
SingletonEagerSafe* eagerInstance2 = SingletonEagerSafe::getInstance();
std::cout << "Eager instances: " << eagerInstance1 << " " << eagerInstance2 << std::endl;
return 0;
}
在这段代码中,我们实现了饿汉式线程安全的单例模式。与懒汉式不同,饿汉式将 instance 变量设置为静态成员,类加载时创建实例。在 getInstance() 函数中,我们直接返回 instance 的地址。由于实例在类加载时创建,因此不需要考虑线程安全问题。
在 main 函数中,我们分别获取了懒汉式和饿汉式单例模式的实例并将其打印到控制台。这将显示相应的实例具有相同的内存地址,证明它们都是单例模式的实现。
7、总结:
总之,单例模式是一种实用的设计模式,适用于需要全局访问点和资源共享的场景。然而,它也存在一些缺点,如扩展性差和对测试不友好。在实际应用中,需要根据实际需求权衡是否使用单例模式。
在实际项目中,单例模式可能会遇到一些挑战。例如,在分布式系统中,需要确保在所有节点上仅有一个实例。为了解决这个问题,可以使用其他技术,如全局服务注册表、分布式锁等。
此外,在面向对象编程中,我们通常希望遵循单一职责原则,即一个类应该仅具有一个原因引起变化。然而,单例模式可能导致一个类承担过多职责,从而违反单一职责原则。因此,在设计和实现单例模式时,需要考虑如何将职责划分得更加清晰。
总之,单例模式在实际项目中可能面临一些挑战。在选择和实现单例模式时,需要充分了解实际需求,以确保模式的适用性和效果。同时,需要权衡单例模式的优缺点,以实现高效、可扩展和可维护的代码。