概述
单例模式
单例模式,是一种常用的软件设计模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
下面是单例模式c++的实现:
class Singleton
{
public:
static Singleton* GetInstance() //实现了共用一个对象
{
if(_instance == nullptr)
{
_instance = new Singleton();
}
return _instance;
}
private: //构造函数,拷贝构造,赋值设为私有成员。限制了在类外构造对象。
Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
private:
static
Singleton * _instance;
};
//静态变量初始化
Singleton* Singleton::_instance = nullptr;
int main()
{
Singleton* a = Singleton::GetInstance();
return 0;
}
上面的代码确实已经实现了单例模式。但是还有一些情况没考虑到,考虑下面两项:
- _instance是使用new在堆上开辟的空间,那么我们需要主动delete它,是否可以让程序主动delete呢?
- 多个线程同时执行GetInstance方法时,有可能会造成new了多个对象。
优化内存释放
针对上面的第一个问题我们一般有两种解决方法:第一种使用智能指针。使用shared_ptr<Singleton>代替Singleton*。
#include <memory>
class Singleton
{
public:
static std::shared_ptr<Singleton> GetInstance() //实现了共用一个对象
{
if (_instance.get() == nullptr)
{
_instance.reset(new Singleton());
}
return _instance;
}
private: //构造函数,拷贝构造,赋值设为私有成员。限制了在类外构造对象。
Singleton() {};
Singleton(const Singleton&) {};
Singleton& operator=(const Singleton&) {};
private:
static std::shared_ptr<Singleton> _instance;
};
//静态变量初始化
std::shared_ptr<Singleton> Singleton::_instance(nullptr);
int main()
{
std::shared_ptr<Singleton> singleton1 = Singleton::GetInstance();
std::shared_ptr<Singleton> singleton2 = Singleton::GetInstance();
return 0;
}
第二种方法是使用另外一个静态对象去释放,我们知道静态对象在程序结束时会被系统进行回收,因此我们可以定义一个内部类 class ReleaseSingleton,它只显示实现析构函数,在析构函数中去释放Singleton中的_instance,然后当我们new Singleton时顺便去构建一个static ReleaseSingleton对象即可。整体思路如下代码:
class Singleton
{
public:
static Singleton* GetInstance() //实现了共用一个对象
{
if (_instance == nullptr)
{
_instance = new Singleton();
//静态对象,当程序结束时会释放这个对象从而调用它的析构函数,这样就可以回收_instance的内存了
static ReleaseSingleton releaseSingleton;
}
return _instance;
}
private:
//只是用来释放_instance的
class ReleaseSingleton
{
public:
~ReleaseSingleton()
{
if (Singleton::_instance != nullptr)
{
delete Singleton::_instance;
Singleton::_instance = nullptr;
}
}
};
private: //构造函数,拷贝构造,赋值设为私有成员。限制了在类外构造对象。
Singleton() {};
Singleton(const Singleton&) {};
Singleton& operator=(const Singleton&) {};
private:
static Singleton* _instance;
};
//静态变量初始化
Singleton* Singleton::_instance = nullptr;
int main()
{
Singleton* singleton1 = Singleton::GetInstance();
Singleton* singleton2 = Singleton::GetInstance();
return 0;
}
多线程问题
并发调用GetIntance有可能导致不同线程拿到不同的Singleton,解决这个问题我们一般有两种解决方法,第一种,在并发调用前构造好Singleton对象(也可以理解在创建其他线程之前,主线程去调用GetInstance)。这样后面任意的线程去调用GetInstance都得到的是之前构造好的对象。那么就不存在上面所描述的问题了。
第二种使用互斥锁:在GetInstance中我们对_instance的操作加锁,这样当有线程在获取Singleton时,其他线程想要去调用被锁住的代码块只能等待正在调用的线程访问完。这样GetInstance就变得有序了,也就解决了上面的问题。
#include <mutex>
#include <future>
class Singleton
{
public:
static Singleton* GetInstance() //实现了共用一个对象
{
{//互斥锁
std::lock_guard<std::mutex> lock(_mutex);
if (_instance == nullptr)
{
_instance = new Singleton();
//静态对象,当程序结束时会释放这个对象从而调用它的析构函数,这样就可以回收_instance的内存了
static ReleaseSingleton releaseSingleton;
}
}
return _instance;
}
private:
//只是用来释放_instance的
class ReleaseSingleton
{
public:
~ReleaseSingleton()
{
if (Singleton::_instance != nullptr)
{
delete Singleton::_instance;
Singleton::_instance = nullptr;
}
}
};
private: //构造函数,拷贝构造,赋值设为私有成员。限制了在类外构造对象。
Singleton() {};
Singleton(const Singleton&) {};
Singleton& operator=(const Singleton&) {};
private:
static std::mutex _mutex;
static Singleton* _instance;
};
//静态变量初始化
Singleton* Singleton::_instance = nullptr;
std::mutex Singleton::_mutex;
int main()
{
std::async([]{
Singleton* singleton = Singleton::GetInstance();
});
std::async([] {
Singleton* singleton = Singleton::GetInstance();
});
return 0;
}
优化
针对上面多线程问题的第二种解决方式,我们可以优化加锁的操作,使用双重判断可以让加锁这个操作只在_intance在new的时候触发,当_intance构造成功后,那么就没有线程安全的问题了,也就不需要互斥锁了。
static Singleton* GetInstance() //实现了共用一个对象
{
if(_instance ==nullptr) //双重判断解决 当_intance构造好后,多个线程无法同时get _intance.
{//互斥锁
std::lock_guard<std::mutex> lock(_mutex);
if (_instance == nullptr)
{
_instance = new Singleton();
//静态对象,当程序结束时会释放这个对象从而调用它的析构函数,这样就可以回收_instance的内存了
static ReleaseSingleton releaseSingleton;
}
}
return _instance;
}
还有一种更简单的写法,使用std::call_once();其保证了函数只被执行一次。其功能类似mutex互斥锁。它可以限制只能执行一次目标函数,并且同时只能有一个线程执行。
#include <mutex>
#include <future>
std::once_flag flag;
class Singleton
{
public:
static Singleton* GetInstance() //实现了共用一个对象
{
std::call_once(flag, []
{
_instance = new Singleton();
//静态对象,当程序结束时会释放这个对象从而调用它的析构函数,这样就可以回收_instance的内存了
static ReleaseSingleton releaseSingleton;
});
return _instance;
}
private:
//只是用来释放_instance的
class ReleaseSingleton
{
public:
~ReleaseSingleton()
{
if (Singleton::_instance != nullptr)
{
delete Singleton::_instance;
Singleton::_instance = nullptr;
}
}
};
private: //构造函数,拷贝构造,赋值设为私有成员。限制了在类外构造对象。
Singleton() {};
Singleton(const Singleton&) {};
Singleton& operator=(const Singleton&) {};
private:
static Singleton* _instance;
};
//静态变量初始化
Singleton* Singleton::_instance = nullptr;
int main()
{
std::async([] {
Singleton* singleton = Singleton::GetInstance();
});
std::async([] {
Singleton* singleton = Singleton::GetInstance();
});
return 0;
}
最后
以上就是爱撒娇蛋挞为你收集整理的c++中如何写出优秀的单例模式的全部内容,希望文章能够帮你解决c++中如何写出优秀的单例模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复