C++单例模式(三种方式)

  单例模式实现要点:

  1. 构造函数私有化 - 为避免其他程序过多建立该类对象,先禁止其他程序建立该类对象
  2. 类中创建一个本类对象 - 本类中自定义对象,让其他程序可以访问
  3. 提供方法获取到该对象 - 方便其他程序对自定义对象的访问 

 单例模式实现方式: 

 1.懒汉式(多线程不安全) - 需要时才进行实例化

#include<iostream>
using namespace std;
#include <windows.h>

//核心的本质就是,只能实例化一个实体
class Singleton {
private:
	Singleton() {}
	Singleton(Singleton&) = delete; //禁止使用
	Singleton& operator=(const Singleton&object) = delete;  //禁止使用


public:
	~Singleton() {}

	//获取一个实例
	static Singleton* getInstance() {

		//如果指针为空,则重新创造一个实例
		if (m_instance_ptr == nullptr) {
			m_instance_ptr = new Singleton;
		}
		//否则,说明已经创建过了,直接返回
		return m_instance_ptr;
	}
	void func()
	{
		cout << "成功实例化了!!!" << endl;
	}
private:

	static Singleton* m_instance_ptr;
};

Singleton* Singleton::m_instance_ptr = nullptr;//在外部对指针进行初始化

int main() {
	static Singleton* instance = Singleton::getInstance();
	instance->func();

	system("pause");
	return 0;
}

问题1:static Singleton* m_instance_ptr; 中的static有什么作用

在这个代码中,static关键字用于声明m_instance_ptr成员变量为静态变量。静态变量是属于类而不是类的实例的变量,因此在整个程序运行期间只会有一个m_instance_ptr变量的实例。这使得在getInstance()方法中可以对该变量进行检查,确保只创建一个Singleton类的实例。

问题2: c++ static是不是就只有一份数据,每次操作都会直接作用在本体上

 是的,C++中的静态数据成员只有一份数据,不会随着对象的创建而分配新的内存空间。每次操作都会直接作用在这份数据上,不会因为对象的创建而产生新的副本。因此,静态数据成员可以被所有对象共享,并且可以在不创建对象的情况下直接访问和修改。

2.懒汉式(多线程安全)-进行加锁 

#include <iostream>
#include <memory> // shared_ptr
#include <mutex>  // mutex

class Singleton {
public:
	typedef std::shared_ptr<Singleton> Ptr;
	~Singleton() {
		std::cout << "析构函数!!!" << std::endl;
	}

	Singleton(Singleton&) = delete; //禁止使用
	Singleton& operator=(const Singleton&) = delete;  //禁止使用

	static Ptr getInstance() {

		if (m_instance_ptr == nullptr) {
			std::lock_guard<std::mutex> auto_lock(m_mutex);
			if (m_instance_ptr == nullptr) {
				m_instance_ptr = std::shared_ptr<Singleton>(new Singleton);
			}
		}
		return m_instance_ptr;
	}

	void use() const {
		std::cout << "已经成功初始化了" << std::endl;
	}


private:
	Singleton() {}
	static Ptr m_instance_ptr;
	static std::mutex m_mutex;
};
Singleton::Ptr Singleton::m_instance_ptr = nullptr;
std::mutex Singleton::m_mutex;

int main() {
	Singleton::Ptr instance = Singleton::getInstance();
	instance->use();

	system("pause");
	return 0;
}

问题1:shared_ptr<Singleton>有什么作用

使用std::shared_ptr<Singleton>可以确保在程序中只有一个实例对象,并且可以方便地在需要时访问该实例对象。std::shared_ptr是智能指针的一种,它可以自动管理对象的生命周期,确保在不再需要时正确地释放对象,避免内存泄漏。在这个示例中,std::shared_ptr<Singleton>确保了只有一个Singleton对象实例,并且在程序结束时正确地释放该实例对象。

问题2: 上面的代码加锁了,难道不用解锁吗

 在上面的代码示例中,确实在获取单例对象实例时使用了std::lock_guard<std::mutex>来加锁,但是没有显式地解锁。这是因为std::lock_guard是一个RAII(资源获取即初始化)类,它在构造时会自动锁住互斥量,在析构时会自动释放锁,即在作用域结束时会自动解锁。

因此,在上面的代码中,当std::lock_guard<std::mutex>对象auto_lock超出作用域时,会自动调用析构函数,从而释放锁,不需要显式地调用解锁操作。这样可以确保在任何情况下都能正确释放锁,避免忘记解锁而导致的死锁问题。

问题3:静态变量都需要在类外进行初始化吗? 

 在C++中,静态成员变量必须在类外进行定义和初始化。这是因为静态成员变量是属于类的,而不是属于类的实例,因此需要在类外进行定义和初始化。

在上面的代码中,singleton::ptrsingleton::m_mutex都是singleton类的静态成员变量,因此需要在类外进行初始化。静态成员变量的初始化必须在全局作用域进行,以确保在程序运行之前就完成了初始化。

通过在全局作用域进行初始化,确保了静态成员变量在程序运行时已经被正确初始化,可以在类的静态成员函数或其他地方使用这些静态成员变量。

问题4: NULL和nullptr有什么区别

 NULLnullptr是C++中用于表示空指针的关键字,它们在一些方面有一些区别:

  1. NULL是在C语言中定义的宏,通常被定义为0或者(void*)0。在C++中,NULL通常被定义为0。因此,NULL实际上是一个整数值的常量。

  2. nullptr是C++11引入的关键字,用于表示空指针常量。nullptr是一个特殊的空指针值,不是整数类型,而是属于nullptr_t类型。因此,nullptr可以更好地表示空指针,避免了一些潜在的问题。

  3. 在C++11及以后的标准中,推荐使用nullptr来表示空指针,而不是NULL。因为nullptr具有更好的类型安全性,可以避免一些潜在的类型转换问题。

综上所述,nullptr是C++11引入的更安全和更明确的表示空指针的关键字,推荐在新的C++代码中使用nullptr来表示空指针。而NULL仍然可以在一些老的代码中使用,但最好在新代码中使用nullptr

 3.饿汉式-提前实例化

#include <iostream>

class Singleton {
public:

	~Singleton() {
		std::cout << "析构函数!!!" << std::endl;
	}

	Singleton(Singleton&) = delete; //禁止使用
	Singleton& operator=(const Singleton&) = delete;  //禁止使用

	static Singleton* getInstance() {
		return m_instance;
	}

	void use() const {
		std::cout << "已经实例化对象了!!!" << std::endl;
	}


private:
	Singleton() {}
	static Singleton* m_instance;

};

//直接在外部就只实例化一个对象,避免的多创建的风险
Singleton* Singleton::m_instance = new Singleton;


int main() {
	Singleton* instance = Singleton::getInstance();
	instance->use();

	system("pause");
	return 0;
}

相关推荐

  1. C++模式方式

    2024-05-10 15:58:07       12 阅读
  2. 模式(五创建方式

    2024-05-10 15:58:07       40 阅读
  3. 模式的几实现方式

    2024-05-10 15:58:07       26 阅读
  4. 模式的几实现方式

    2024-05-10 15:58:07       19 阅读
  5. 模式的几实现方式

    2024-05-10 15:58:07       11 阅读
  6. 创建模式的六方式

    2024-05-10 15:58:07       8 阅读
  7. 模式

    2024-05-10 15:58:07       27 阅读
  8. 模式的一C++写法

    2024-05-10 15:58:07       34 阅读
  9. 模式C#】

    2024-05-10 15:58:07       37 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-10 15:58:07       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-10 15:58:07       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-10 15:58:07       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-10 15:58:07       18 阅读

热门阅读

  1. python列表生成式学习

    2024-05-10 15:58:07       8 阅读
  2. 仿真相关知识积累

    2024-05-10 15:58:07       9 阅读
  3. 国内不同领域对应的AI简谈

    2024-05-10 15:58:07       9 阅读
  4. Python 基础入门

    2024-05-10 15:58:07       10 阅读
  5. [GStreamer][LearnGst] setup.sh 脚本

    2024-05-10 15:58:07       13 阅读
  6. 【论文合集 2】- 基于忆阻器的存内计算

    2024-05-10 15:58:07       11 阅读
  7. MySql开源闪回工具MyFlash —— 筑梦之路

    2024-05-10 15:58:07       13 阅读
  8. 少的缓存穿透是缓存击穿,大量的是缓存雪崩

    2024-05-10 15:58:07       13 阅读
  9. Redis

    2024-05-10 15:58:07       12 阅读
  10. ORACLE 生成AWR常用脚本

    2024-05-10 15:58:07       9 阅读
  11. C#中override与重载的区别

    2024-05-10 15:58:07       11 阅读