单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例特点:
1 在任何情况下,单例类永远只有一个实例存在。
2 单例需要有能力为整个系统提供这一唯一实例。
示例:打印机,任务管理器等。
分为两种:
饿汉模式和懒汉模式
饿汉模式:
实例对象在程序启动时就已经创建了
理解:太饿了,等不及要吃了
懒汉模式:
实例对象只有在第一次使用时才会被创建
理解:太懒了,要用了,才做吃的
饿汉模式的优点是线程安全,因为,不需要担心多个线程同时创建实例导致的线程安全问题。但是,它的缺点是可能会浪费内存,因为实例对象在程序启动时就已经创建了,即使在程序运行过程中没有使用它。
懒汉模式的优点是节省内存,因为实例对象只有在第一次使用时才会被创建。但是,它的缺点是可能会存在线程安全问题,因为多个线程可能同时访问实例对象,导致创建多个实例的问题。
对于一个类只能创建一个对象,对应的就是设计模式当中的单例模式
思路:
- 限制构造函数(只要禁用析构,即将其私有化,所有构造都不可以在类外用了)和析构函数(其实本质来说限制析构函数就可以了),说白了就是将构造、析构定义为私有
- 提供一个公有的函数访问构造函数(此时构造【私有的】只能在类里面使用,所以应该在类中创建对象),该公有函数需要限制访问次数(),且该函数不需要对象就能访问(因此定义为静态函数,其返回值为指针和引用,不能返回栈区內存,可以返回堆区(new)和静态全局区(static))
- 既然返回值为堆区或者静态全局区,则接收该申请的內存要么静态变量或者堆区new的內存;
举例实现:
注:格式其实应该是类声明在头文件.h
中,类实现在.cpp
中,此处是为了例子更容易理解
懒汉模式:
#include<iostream>
using namespace std;
//单例类
class Singleton
{
public:
/*此处的返回值可以是类的指针类型或者引用类型,
但绝对不能是类类型,否则会调用拷贝,此时对象会增加变为2个*/
static Singleton* GetSingleton()
{
//此处为懒人模式
//计数器加1(使用该对象的人+1)
++count;
//判断指针是否为空
if (nullptr == pSingleton)
{
//选择返回堆区內存 释放会比较麻烦
pSingleton = new Singleton;
}
//返回值为指针类型或者引用类型,不能返回栈区的变量,只能返回堆区或者静态全局区的变量
return pSingleton;
}
//单例模式对象的释放是不可以在析构里面的,否则会造成递归调用析构,所以要自己写释放函数
void DeleteSingleton()
{
/*自己写也需要注意,一般来叔,如果存在好几个地方都在用我的单例对象,此
时其中任意一个单例內存释放了,其余在使用的【因为本质就一个对象】全部会
释放掉。
为了解决此问题现在优化:
新增一个计数器(静态数据成员,所有对象共享,属于类),当其为0时,调用释放函数可以释放,
不为0时,则不可以释放
*/
//计数器减1(使用该对象的人-1)
--count;
//正真释放內存
if (0 == count)
{
delete pSingleton;
//指针置空(delete释放指针所指內存后,一定要置空,避免出现野指针,这里的置空原因是为了下一次单例对象创建使用)
pSingleton = nullptr;
}
}
private:
//构造函数私有,主要用于对对象初始化
Singleton()
{
}
//将析构定义为私有成员,则类外部所有构造函数都不可以用,即类外不可以创建对象,所以析构私有是关键
~Singleton()
{
}
//声明静态数据成员,用于接收该对象的地址
static Singleton *pSingleton;
//计数器,用于做正真释放判断
static int count;
};
//静态数据成员必须要初始化
Singleton *Singleton::pSingleton = nullptr;
int Singleton::count = 0;
//主函数使用
int main()
{
//报错,因为构造不可用
//Singleton A;
//刚开始没对象,用函数创建对象
Singleton *B = Singleton::GetSingleton();
/*拷贝构造虽然没有被定义为私有,但析构函数定义为私有了,
准确来说,因为析构被私有,类外不可访问,导致所有构造在类
外都被禁用了【因为析构和构造必须成对】*/
//Singleton C = *B;
//用户D也使用
Singleton *D = Singleton::GetSingleton();
//注意:要保证D和B一者不想用了,另一方还可以继续使用
//请看类成员函数DeleteSingleton()的定义
cout << "D指向对象的地址:" << D << endl;
//D不想用了,B应该还能使用
cout << "D不用了" << endl;
D->DeleteSingleton();
//此时D指向內存被释放,指针要置空
D = nullptr;
cout << "B指向对象的地址:" << B << endl;
//B也不想用了,地址置空,现在就2人在用
cout << "B不用了" << endl;
B->DeleteSingleton();
//此时B指向內存被释放,指针要置空
B = nullptr;
cout << "B指向对象的地址:" << B << endl;
return 0;
}
饿汉模式:
#include<iostream>
using namespace std;
class Singleton
{
public:
static Singleton* GetSingleton()
{
//饿汉模式
//此处比较简单 编译时其实就已经创建了新对象,后面再运行都是第一次创建的对象
//其次并不需要考虑释放的问题,静态局部变量程序执行结束,系统自动回收內存
//內存在静态全局区
static Singleton singleton;
return &singleton;
}
private:
//析构、构造禁用
Singleton()
{
}
~Singleton()
{
}
};
int main()
{
//刚开始没对象,用函数创建对象
Singleton *B = Singleton::GetSingleton();
//用户D也使用
Singleton *D = Singleton::GetSingleton();
//注意:要保证D和B一者不想用了,另一方还可以继续使用,但这里和上面不太一样
//此时实现原理是,其实它只要程序在运行中,该对象其实一直存在
cout << "D指向对象的地址:" << D << endl;
//D不想用了,B应该还能使用
cout << "D不用了" << endl;
//此时D指向內存并未释放,但要回收访问的权限,所以指针要置空
D = nullptr;
cout << "B指向对象的地址:" << B << endl;
//B也不想用了,地址置空,现在就2人在用
cout << "B不用了" << endl;
//此时B指向內存并未释放,但要回收访问的权限,所以指针要置空
B = nullptr;
cout << "B指向对象的地址:" << B << endl;
cout << "其内存一直都在:" << Singleton::GetSingleton() << endl;
return 0;
}
例题:
实现一个类,保证该类只能创建一个对象 点类Point,静态成员输出count的值,用成员函数输出点的信息