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

单例模式实现要点:

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

单例模式实现方式:

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

cpp 复制代码
#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.懒汉式(多线程安全)-进行加锁

cpp 复制代码
#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.饿汉式-提前实例化

cpp 复制代码
#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;
}
相关推荐
rjszcb31 分钟前
一文说完c++全部基础知识,IO流(二)
c++
小字节,大梦想1 小时前
【C++】二叉搜索树
数据结构·c++
吾名招财1 小时前
yolov5-7.0模型DNN加载函数及参数详解(重要)
c++·人工智能·yolo·dnn
我是哈哈hh2 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
憧憬成为原神糕手2 小时前
c++_ 多态
开发语言·c++
郭二哈2 小时前
C++——模板进阶、继承
java·服务器·c++
挥剑决浮云 -2 小时前
Linux 之 安装软件、GCC编译器、Linux 操作系统基础
linux·服务器·c语言·c++·经验分享·笔记
丶Darling.2 小时前
LeetCode Hot100 | Day1 | 二叉树:二叉树的直径
数据结构·c++·学习·算法·leetcode·二叉树
labuladuo5203 小时前
Codeforces Round 977 (Div. 2) C2 Adjust The Presentation (Hard Version)(思维,set)
数据结构·c++·算法
小柯J桑_3 小时前
C++:STL简介
c++·stl