欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > C++单例模式

C++单例模式

2024/11/30 8:41:02 来源:https://blog.csdn.net/weixin_67131528/article/details/143214357  浏览:    关键词:C++单例模式

在编程的时候,有一些场景经常需要我们保证这个类只能存在一个对象,这就叫做单例模式。

什么是单例模式?

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个
访问它的全局访问点,该实例被所有程序模块共享 。比如在某个服务器程序中,该服务器的配置
信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再
通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

单例模式的分类

单例模式的实现一般分为饿汉模式和懒汉模式。

饿汉模式

所谓饿汉模式,就是“饿”了,急需要吃,即创建出对象,就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。

懒汉模式

所谓懒汉模式,是指比较“懒”,第一个人需要使用时再去实例对象。

实现单例模式

下面我们来实现一下单例模式,以便更深刻了解

饿汉模式

因为需要一个类只能存在一个对象,所以首先我们需要把构造函数私有吧,避免外面有人直接去实例化对象,其次也需要把拷贝构造封掉,因为可能会有人拿唯一的对象去拷贝构造出一个新的,那就还有赋值重载。

#include<iostream>
#include<vector>using namespace std;class hungersingle
{
private:hungersingle(int x = 10, int y = 50, vector<string> vv = { "你好","我不好" }):_x(x), _y(y), vs(vv){cout << "hunfersingle构造" << endl;}hungersingle(const hungersingle&) = delete;hungersingle operator=(const hungersingle&) = delete;int _x;int _y;vector<string> vs;
};

而我们需要保证在程序运行前只能存在一个对象,那这个对象必须是“全局”的,所以我们可以用一个静态成员变量,该类只有一个静态成员。

static hungersingle hs;

我们提供一个接口去获取该静态成员即可

static hungersingle* getobject()
{return &hs;
}

这样一个饿汉单例模式就好啦,饿汉是比较简单的。再给他提供一些接口测试一下

class hungersingle
{
public:static hungersingle* getobject(){return &hs;}void Print(){cout << _x << endl;cout << _y << endl;for (auto& e : vs)cout << e << " ";cout << endl;}void addstr(const string& str){vs.emplace_back(str);}void changeINT(int m, int n){_x = m;_y = n;}hungersingle(const hungersingle&) = delete;hungersingle operator=(const hungersingle&) = delete;private:hungersingle(int x = 10, int y = 50, vector<string> vv = { "你好","我不好" }):_x(x), _y(y), vs(vv){cout << "hunfersingle构造" << endl;}int _x;int _y;vector<string> vs;static hungersingle hs;//类内声明
};
hungersingle hungersingle::hs;//类外面定义

饿汉单例模式虽然简单,但是也是会有一些缺点的,因为它需要在程序启动时main函数之前就把对象实例化出来,我们这个类比较简单,如果有的类实例化对象时需要申请大量资源,那么程序运行后半天实例化不出对象,可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。

懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取
文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,
就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式更好。

懒汉模式的核心思想是有一个静态成员指针,第一个用它的人去实例化。

首先它也需要构造函数私有,封掉拷贝构造和复制重载

#include<iostream>
#include<vector>
#include<mutex>
#include<string>
using namespace std;class lazysingle
{lazysingle(const lazysingle&) = delete;lazysingle operator=(const lazysingle&) = delete;private:lazysingle(int x = 10, int y = 50, vector<string> vv = { "你好","我不好" }):_x(x), _y(y), vs(vv){}~lazysingle(){cout << "~lazysingle()" << endl;}int _x;int _y;vector<string> vs;static lazysingle* ls_ptr;
};
lazysingle* lazysingle::ls_ptr = nullptr;

刚才说了懒汉的思想是第一个人去实例化对象,即

	static lazysingle* getobject(){if (ls_ptr == nullptr){ls_ptr = new lazysingle;}return ls_ptr;}

但是我们需要注意的是,如果是在多线程场景下,这是有线程安全问题的,因为如果两个执行流几乎同时执行if (ls_ptr == nullptr),第一个执行流刚进到if内部,因为时间片到了被切出去,第二个执行流又去判断if (ls_ptr == nullptr)也进去就会出问题。所以这里需要加锁保证安全。

	static lazysingle* getobject(){if (ls_ptr == nullptr){unique_lock<mutex> lock(_mx);//用锁防止同时有人来申请if (ls_ptr == nullptr)ls_ptr = new lazysingle;}return ls_ptr;}

因为懒汉这个这个对象是new出来的,所以不要忘记delete,为了防止内存泄漏,可以托管给其它对象处理,即

static void Destroy()
{if (ls_ptr){delete ls_ptr;ls_ptr = nullptr;}}class Gc//定义这个类让他的析构自动调用lazysingle的destroy,防止忘记调用
{
public:~Gc(){lazysingle::Destroy();}
};
static Gc gc;

先写一个destroy函数调用delete,再写一个内部类,创建一个静态成员变量,当程序结束时该变量调用析构自动去调用lazysingle::Destroy();完整代码如下:

class lazysingle
{
public:static lazysingle* getobject(){if (ls_ptr == nullptr){unique_lock<mutex> lock(_mx);//用锁防止同时有人来申请if (ls_ptr == nullptr)ls_ptr = new lazysingle;}return ls_ptr;}void Print(){cout << _x << endl;cout << _y << endl;for (auto& e : vs)cout << e << " ";cout << endl;}static void Destroy(){if (ls_ptr){delete ls_ptr;ls_ptr = nullptr;}}void addstr(const string& str){vs.emplace_back(str);}void changeINT(int m, int n){_x = m;_y = n;}lazysingle(const lazysingle&) = delete;lazysingle operator=(const lazysingle&) = delete;private:lazysingle(int x = 10, int y = 50, vector<string> vv = { "你好","我不好" }):_x(x), _y(y), vs(vv){}~lazysingle(){cout << "~lazysingle()" << endl;}int _x;int _y;vector<string> vs;static lazysingle* ls_ptr;static mutex _mx;class Gc//定义这个类让他的析构自动调用lazysingle的destroy,防止忘记调用{public:~Gc(){lazysingle::Destroy();}};static Gc gc;
};
mutex lazysingle::_mx;
lazysingle* lazysingle::ls_ptr=nullptr;
lazysingle::Gc lazysingle::gc;

main函数执行下面语句

int main()
{lazysingle::getobject()->Print();return 0;
}

C++ 11 对static局部变量的初始化语义进行了改进,使得在多线程环境下,static局部变量的初始化是线程安全的,即原子的。

所以C++11之后可以对懒汉模式不加锁实例化静态局部对象

class lazysingle
{
public:static lazysingle* getobject(){static lazysingle laz;//定义局部静态对象,只有第一次调用这个函数的时候才会创建这个对象//C++11后这是一步原子的操作return &laz;}void Print(){cout << _x <<endl;cout << _y << endl;for (auto& e : _vs)cout << e << " ";cout << endl;}void addstr(const string& ret){_vs.push_back(ret);}void addINT(int m,int n){_x = m;_y = n;}lazysingle(const lazysingle&) = delete;lazysingle operator=(const lazysingle&) = delete;private:lazysingle(int x=100,int y=200,vector<string> vs={"高考","中考","殿试"}):_x(x),_y(y),_vs(vs){cout << "lazysingle构造" << endl;}int _x;int _y;vector<string> _vs;
};

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com