中级C++:单例模式

写一个不能被拷贝和复制的类

  • 拷贝和赋值重载函数私有化。
  • 分C++98和C++11写法;推荐C++11写法。
//C++98
class CopyBan
{
 // ...
 
private:
 CopyBan(const CopyBan&);
 CopyBan& operator=(const CopyBan&);
 //...
};

//C++11
class CopyBan
{
 // ...
 CopyBan(const CopyBan&)=delete;
 CopyBan& operator=(const CopyBan&)=delete;
 //...
};

原因

  1. 设置成私有:如果只声明,没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
  2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
  3. C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
    在这里插入图片描述

请设计一个类,只能在堆上创建对象

  1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
  2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
class MyClass
{
public:
	static MyClass* get_init()
	{
		return new MyClass;
	}

private:
	MyClass()  
		:_a(0)
	{
	}
	MyClass(const MyClass&)=delete;
	MyClass& operator=(const MyClass&) = delete;
	int _a;
};

static:

  1. 静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;
  2. 而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
    在这里插入图片描述
    在这里插入图片描述
  • 防止在栈上进行拷贝构造。

请设计一个类,只能在栈上创建对象

  • 方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可。
class MyClass1
{
public:
	static MyClass1 get_init()
	{
		return MyClass1();
	}
	
private:
	MyClass1(){}
	MyClass1(const MyClass1&) = delete;
	MyClass1& operator=(const MyClass1&) = delete;
	int _a;
};

在这里插入图片描述

  • 方法二:屏蔽new:因为new在底层调用void* operator new(size_t size)函数,只需将该函数屏蔽掉即可。
class MyClass
{
public:
	MyClass() {}
private:
	void* operator new(size_t x) = delete;
	void operator delete(void* ptr) = delete;
};

在这里插入图片描述

原因:

  1. 有专属的new会调用专属的,向内存池申请,效率更高。
  2. 没有专属的会调用全局的operator new 向系统申请内存。
  3. 我写了你就要调用我…,但是你调用到了,我让你用不了
  • **虚表:**虚函数表在编译时生成,虚表指针是在 构造函数初始化列表 初始化。

请设计一个类,不能被继承

  • C++98
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
 static NonInherit GetInstance()
 {
 return NonInherit();
 }
private:
 NonInherit()
 {}
};

C++11:

  • final关键字,final修饰类,表示该类不能被继承。
class A final
{
 // ....
};

比较:

  1. C++98可以被继承,但继承的派生类不能创建对象;
  2. 派生类的对象构造函数必须调用基类的,但基类的私有不可见;所以继承不会报错,创建对象会报错。

设计模式

设计模式:

  1. 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
  2. 使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。
  3. 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
  4. 单例模式,工厂模式,观察者模式:迭代器模式 、适配器模式等等。

设计模式详细介绍



请设计一个类,只能创建一个对象(单例模式)

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

  • 假如定义一个全局对象,在所有文件都是可见的,只能定义在一个头文件里,多个源文件包含时,每个源文件预处理时头文件展开,链接会报错,重定义错误。
  • 假如定义一个静态全局对象,只在每个源文件内部可见,都有一份不冲突,但无法保证时同一个。
  • 对于C语言,可以在头文件extern声明全局对象,别的原文件定义;软性约束,使用场景有限。
。h
extern std::vector<int> v;  //全局对象声明,别的源文件定义
。cpp
std::vector<int> v;
  • pragma once等于条件编译,有多个头文件时,只展开一份。

饿汉模式

  • 就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。(在 main 函数之前就创建初始化)
  • 优点:简单
  • 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例存在依赖关系时,启动顺序不确定。
  • 启动慢:如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。

实现:

  1. 构造函数私有化,不能随意创建对象
  2. 声明一个私有的静态类型对象
  3. 写一个获取初私有对象的静态函数
  4. 声明与定义分开
。h
class MyClass
{
public:
	
	//3. 写一个获取初始化的静态函数
	static  MyClass& get_init();
	vector<int> _vi;//想把vector对象私有,再包一层对象的插入等等
	/*void PushBcak(size_t x)
	{
		return _vi.push_back(x);
	}*/
private:
	//1.构造函数私有化,不能随意创建对象
	MyClass(){}

	//防拷贝
	MyClass(const MyClass&) = delete;
	MyClass& operator=(const MyClass&) = delete;

	//2.声明一个私有的静态类型对象
	static MyClass _my;
};
.cpp
MyClass MyClass::_my;

MyClass& MyClass::get_init()
{
	return _my;
}

test:
int main()
{
	MyClass::get_init()._vi.push_back(222);
	MyClass::get_init()._vi.push_back(24);
	MyClass::get_init()._vi.push_back(34);
	for (auto& i : MyClass::get_init()._vi)
	{
		cout << i << endl;
	}
	return 0;
}


懒汉模式

  • 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
  • 缺点:复杂

实现:

  1. 构造函数私有化,不能随意创建对象
  2. 声明一个私有的静态类型指针对象
  3. 写一个获取私有对象的静态函数
  4. 声明与定义分开
  5. 要加双层锁,保证线程安全和效率;避免线程数据被覆盖、线程频繁上下文切换和阻塞。
。h
class MyClass  //静态成员函数,成员变量 声明定义分开
{
public:

	//3. 写一个获取初始化的静态函数
	static  MyClass* get_init();
	vector<int> _vi;//想把vector对象私有,再包一层对象的插入等等
	/*void PushBcak(size_t x)
	{
		return _vi.push_back(x);
	}*/
private:
	//1.构造函数私有化,不能随意创建对象
	MyClass() {}

	//放拷贝
	MyClass(const MyClass&) = delete;
	MyClass& operator=(const MyClass&) = delete;

	//2.声明一个私有的静态类型指针对象
	static MyClass* _my;
	static mutex _tex;//互斥锁
};

,cpp
MyClass* MyClass::_my = nullptr;
mutex MyClass::_tex;
MyClass* MyClass::get_init()
{
	if (_my==nullptr)//双锁保证了线程效率,避免了线程频繁的上下文切换,来回阻塞
	{
		_tex.lock();
		if (_my == nullptr)
		{
			_my = new MyClass;
		}
		_tex.unlock();  //枷锁保证线程安全,避免数据被覆盖。
	}

	return _my;
}

懒汉模式测试用例

1.cpp
void f1()
{
	MyClass* pm = MyClass::get_init();
	pm->_vi.push_back(21);
	pm->_vi.push_back(22);
	pm->_vi.push_back(23);
	cout << &pm->_vi << endl;
	for (auto& i : pm->_vi)
	{
		cout << i << endl;
	}
	cout << endl;
}.cpp
int main()
{
	f1();
	MyClass* pm = MyClass::get_init();
	pm->_vi.push_back(24);
	pm->_vi.push_back(25);
	pm->_vi.push_back(26);
	cout << &pm->_vi << endl;
	for (auto& i : pm->_vi)
	{
		cout << i << endl;
	}

	//MyClass1 m1 = MyClass1::get_init();
	return 0;
}

在这里插入图片描述


单例模式对象的销毁

  • 饿汉模式,由于对象是静态的,想释放也释放不了,main函数结束以后,意味着整个进程就结束了,系统会对整个进程的资源进行清理。
  • 懒汉模式,同上,不用手动释放问题不大:手动如下:
void MyClass::Delmc()
{
	if (_my != nullptr)//双锁保证了线程效率,避免了线程频繁的上下文切换,来回阻塞
	{
		_tex.lock();
		if (_my!=nullptr)
		{
			delete _my;
			_my = nullptr;
		}
		_tex.unlock();  //枷锁保证线程安全,避免数据被覆盖。
	}
}

展望

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值