返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++深入详解单例模式与特殊类设计的实现
  • 481
分享到

C++深入详解单例模式与特殊类设计的实现

2024-04-02 19:04:59 481人浏览 八月长安
摘要

目录单例模式什么是单例模式应用场景优缺点实现饿汉模式懒汉模式特殊类设计设计一个类只能在堆上创建对象方法一方法二只能在栈上创建对象方法一方法二一个类不能被继承最后单例模式 什么是单例模

单例模式

什么是单例模式

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。–大话设计模式

应用场景

保证一个类只有一个实例

  • windows下的任务管理器,回收站等。
  • 日志管理,计数器等。

简而言之,你需要唯一实例时就可以考虑单例模式。这样它可以严格地控制客户怎样访问即何时访问它,即对唯一实例的受控访问。

优缺点

优点

  • 减少内存开销,因为在系统中只有一个实例。
  • 避免了频繁的创建和销毁对象,提高了性能
  • 避免对资源的多重占用,比如单例时多人只写一个日志文件,如果有多个日志文件可能导致对相同的日志文件进行写操作
  • 设置全局访问点

缺点

  • 职责过重,与单一职责存在冲突
  • 不能继承(构造方法私有)

实现

单例模式有两种实现模式,懒汉模式和饿汉模式。

注意单例模式的概念,大概就是唯一实例且有全局访问点,我们从这两点入手。

唯一实例:构造函数私有+防拷贝

拷贝构造是一种构造方式,所以需要防止,构造函数私有不让别人new。

全局访问点:给一个公共的接口

饿汉模式

简单来说,把东西一开始就做好,需要的时候直接吃。

#include <iOStream>
using namespace std;
class Singleton
{
public:
	static Singleton& GetInstance()
	{
		return _instance;
	}
	int GetRandom()//由于侧重点不是随机数 所以直接返回一个30
	{
		return 30;
	}
private:
	Singleton() {}//构造函数私有
	Singleton(Singleton&) = delete;//防拷贝,被=delete修饰表明这个函数被删除,即可以只声明不实现,换言之禁用了该函数
	Singleton& operator=(const Singleton&) = delete;//防拷贝
	static Singleton _instance;
};
Singleton Singleton::_instance;//类外必须初始化,类内只是声明
int main()
{
	//1.调用
	cout<<Singleton::GetInstance().GetRandom()<<endl;
	//2.
	Singleton& s = Singleton::GetInstance();
	cout<<s.GetRandom()<<endl;
	return 0;
}

通过防拷贝和构造函数私有化之后下面的几种办法都失效了

Singleton s;//err
Singleton s1(s);//err
Singleton s1=s;//err

从上面我们可以看出饿汉模式的优缺点了,有点显而易见就是实现很简单粗暴,缺点很明显,类加载时单例对象就已经生成了,即还没有用就已经加载出来了,比如这个资源很大,又在游戏启动时加载,那就会造成游戏启动很慢。且如果有多个单例对象启动时实例化顺序不确定(不同源文件类内的单例对象实例化顺序是不确定的,懒汉模式解决了这个问题,因为懒汉模式的实例化在函数内部,可以通过调用函数的顺序来解决实例化的顺序问题)。

类加载时静态初始化解决了线程安全问题。

懒汉模式

什么时候需要就什么时候做饭,然后吃。

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		if (_instance == nullptr)
		{
			_mtx.lock();//double lock保证线程安全
			if (_instance == nullptr)//必须再次检查  不然可能另一个线程那已经new完了,这边又new违背了单例,且可能覆盖数据。
			{
				_instance = new Singleton();
			}
			_mtx.unlock();
		}
		return _instance;
	}
	int GetRandom()
	{
		return 30;
	}
	class Clear//资源回收的内部类,必须是公有的,不然外部声明报错
	{
	public:
		~Clear()
		{
			cout << "释放资源" << endl;
			delete _instance;
		}
	};
private:
	static Clear _cle;
	Singleton() {}
	Singleton(Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	static Singleton* _instance;
	static mutex _mtx;//线程安全
};
Singleton* Singleton::_instance = nullptr;
mutex Singleton::_mtx;
Singleton::Clear _cle;
int main()
{	
	//1.
	cout<<Singleton::GetInstance()->GetRandom()<<endl;
	//2/
	static Singleton* s = Singleton::GetInstance();
	cout << s->GetRandom() << endl;
	//不能用左值引用接收的原因  返回值是右值 得用右值引用接收
	//为什么函数返回值是右值  因为返回值是借助临时变量返回的拿不到地址
	//如果返回值是左值引用就能用左值引用接收
	return 0;
}

懒汉模式的优缺点也很明显,优点是什么时候第一次用就什么时候实例化,此外可以通过调用函数解决多个单例对象实例化的顺序问题,缺点就是写起来复杂,要考虑线程安全和内存泄露方面的问题。懒汉采用的是指针也更好回收资源(饿汉采用的是对象)

懒汉可能因为多线程丢数据,线程加保证在多线程环境下一定只有一个线程去new对象,只创建出一个单例对象,加锁可能导致频繁切换上下文,double lock解决。

特殊类设计

我们一般会通过构造函数,拷贝构造和赋值重载创建对象,现在把这几种方法全禁用了,然后自己再写一个外部可调用的接口来自定义类的创建方式。

当然也有别的办法,这种经常可以作为一种通解思路。

下面的禁用采用c++11的delete关键字实现,作用是禁止编译器生成默认的函数版本,即有声明但无实现。

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

简单来说只能通过new来创建对象。

方法一

构造方法禁用,然后给一个接口让外部调用。

构造方法的禁用=构造函数私有+禁用拷贝构造+禁用赋值重载

class HeapOnly
{
public:
	static HeapOnly* GetInstance()
	{
		return new HeapOnly;
	}
	void Test()
	{
		cout << "I am Test" << endl;
	}
private:
	HeapOnly() {}
	HeapOnly(HeapOnly&) = delete;
	HeapOnly& operator=(const HeapOnly&) = delete;
};
int main()
{	
	HeapOnly* ho = HeapOnly::GetInstance();
	ho->Test();
	delete ho;
	return 0;
}

方法二

析构函数私有

对象建立在栈上,是由编译器分配空间的,编译器管理对象的生命周期,对象使用完后编译器会检查这个对象所有的非静态函数,包括析构函数,当编译器发现析构函数不能访问后就不能回收这块空间,所以编译器无法为其分配空间,编译器检查到这种情况也会报错。

析构函数私有的方法不建议使用,因为在类外无法使用delete释放空间,容易造成内存泄漏。

class HeapOnly
{
public:
	void Test()
	{
		cout << "I am Test" << endl;
	}
private:
	~HeapOnly();
};
int main()
{	
	//HeapOnly ho_stack;//err
	HeapOnly* ho = new HeapOnly;
	ho->Test();
	return 0;
}

只能在栈上创建对象

方法一

不能使用new --> 重载operator new即可。

这种存在一个缺陷,就是仍然可以在静态区创建对象

方法二

将构造函数设为私有再自定义一个接口

这里不用禁用构造函数,拷贝构造,赋值重载,因为对于下面的场景来说,匿名对象的拷贝构造更符合场景,编译器会选拷贝构造来进行构造,所以如果我们禁用了拷贝构造会报错,因为我们的delete关键字是有声明无实现,并不是真的把这个函数连带声明删除了。所以StackOnly()一看有我们自己写的拷贝构造的声明就会去匹配这个拷贝构造,我们自己写的拷贝构造又没有实现拷贝功能就会报错。

理解这个和编译链接知识有关,编译器看到声明就去匹配了,而不是一定要看到函数实现才匹配,链接时候才会去找实现,当发现有声明无实现时就很容易导致链接错误。

一个类不能被继承

父类构造函数私有即可,构造时先构造父类再构造子类,父类构造不出不能继承。

最后

关于单例模式,可以说是只能创建一个对象的实现。

还有个小问题,为什么不用全局变量代替单例模式,全局定义一个唯一的变量不就行了,合理吧[doge],理论上可以,但是非常不建议,可以自行查找资料"为什么不建议使用全局变量"。

全局变量的使用可能带来很多问题,而且很容易造成链接、重定义等错误,如果多人协作时有人再给这个变量起个别名,那维护代码时代价就太大了,这还只是全局变量的一个小缺点。

牛客代码规范评分里也不建议用全局变量,当然写题时代码没那么长,几个全局变量倒时不打紧。

.h不能包含定义,不然多个cpp去包含就会有链接错误,尽量把定义和声明分离开来

【82】【Cherno C++】【中字】C++的单例模式_哔哩哔哩_bilibili

GitHub代码汇总

到此这篇关于C++深入详解单例模式与特殊类设计的实现的文章就介绍到这了,更多相关C++单例模式内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++深入详解单例模式与特殊类设计的实现

本文链接: https://lsjlt.com/news/151285.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++深入详解单例模式与特殊类设计的实现
    目录单例模式什么是单例模式应用场景优缺点实现饿汉模式懒汉模式特殊类设计设计一个类只能在堆上创建对象方法一方法二只能在栈上创建对象方法一方法二一个类不能被继承最后单例模式 什么是单例模...
    99+
    2024-04-02
  • C++特殊类设计概念与示例讲解
    目录一、设计模式概念二、设计一个不能被拷贝的类三、设计一个只能在堆上创建对象的类3.1 私有构造3.2 私有析构四、设计一个只能在栈上创建对象的类五、设计不能被继承的类六、单例模式6...
    99+
    2023-05-17
    C++特殊类设计 C++设计模式
  • 深入理解Java设计模式之单例模式
    目录一、什么是单例模式二、单例模式的应用场景三、单例模式的优缺点四、单例模式的实现1.饿汉式2.懒汉式3.双重加锁机制4.静态初始化五、总结一、什么是单例模式 单例模式是一种常用的软...
    99+
    2024-04-02
  • C++设计模式之单例模式详解
    目录单例模式:就是只有一个实例。单例模式又分为两种基本的情形:饿汉式和懒汉式如下是懒汉式单例类小结:继续看单例模式总结单例模式:就是只有一个实例。 singleton pattern...
    99+
    2024-04-02
  • C++单例设计模式详细讲解
    目录特殊类设计只能在堆上创建对象的类请设计一个类只能在栈上创建对象请设计一个类不能被拷贝请设计一个类不能被继承请设计一个类只能创建一个对象(单例模式)懒汉模式和饿汉模式的对比特殊类设...
    99+
    2024-04-02
  • java设计模式-单例模式实现方法详解
    目录饿汉式静态变量静态代码块懒汉式线程不安全线程安全双重检查静态内部类总结单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要...
    99+
    2024-04-02
  • Java设计模式之单例模式实例详解【懒汉式与饿汉式】
    本文实例讲述了Java设计模式之单例模式。分享给大家供大家参考,具体如下:单例模式就是产生一个对象实例,供外外部访问。它的应用场景就是在这个类在全局真资源需要统一访问,否则会造成混乱时,才有必要设计成单例。懒汉式,就是在使用这个对象时,才去...
    99+
    2023-05-31
    java 设计模式 单例模式
  • C++设计模式之简单工厂模式的实现示例
    前言 在我们要使用一个对象时,就必须通过类来实例化对象,也就是需要new一个对象。在new的过程是非常复杂的,要经过读文件->解析文本->创建对象->给属性设值等过...
    99+
    2024-04-02
  • 深入了解PHP trait DTO的设计模式与实践
    Introduction:在PHP开发中,设计模式是必不可少的一部分。其中,DTO(Data Transfer Object)是一种常用的设计模式,用于封装数据传输的对象。而在实现DTO的过程中,使用trait(特征)可以有效地提高代码的复...
    99+
    2023-10-21
    PHP dto trait
  • C#设计模式之装饰器模式实例详解
    最近踢了场球,9人制比赛,上半场我们采用防守阵型效果不佳,下半场采用进攻阵型取得了比赛的主动。我们上下半场所采取的策略,似乎可以用"装饰器"模式实现一遍。 首先肯...
    99+
    2024-04-02
  • Java设计模式的单例模式如何实现
    这篇文章主要介绍了Java设计模式的单例模式如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java设计模式的单例模式如何实现文章都会有所收获,下面我们一起来看看吧。单例模式单例模式顾名思义就是单一的实例...
    99+
    2023-06-29
  • java 中设计模式(装饰设计模式)的实例详解
    java 中设计模式(装饰设计模式)的实例详解应用场景:在不对原有对象类进行修改的基础上,给一个或多个已有的类对象提供增强额外的功能. 我觉得可以从字面理解,装饰,装饰房子。房子可以看成原有的类。等于你把一个已经建好的房子按照自己的想法再装...
    99+
    2023-05-31
    java 装饰模式 ava
  • C++实现设计模式之装饰者模式详解
    目录设计模式和设计原则装饰者模式中的类案列描述代码实现总结设计模式和设计原则 装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者模式提供了比继承更有弹性的替代方案。 装饰者模式...
    99+
    2024-04-02
  • 最新SS园设计模式实例剖析与深入解读教程
    │  readme.txt│  ├─创建型模式│      设计模式与UML之创建型模式(第一讲 建造者模式).exe│     ...
    99+
    2023-06-03
  • Java中常用的设计模式之单例模式详解
    目录注意优点缺点使用场景一、实现方式二、实现方式三、测试总结注意 1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单例类必须给所有其他对象提供这一实例。 优点...
    99+
    2024-04-02
  • c/c++单例模式类的混合编译案例详解
    目录C/C++混合编译解决方案:中间层调用log案例解决方案:源代码C/C++混合编译 难点:c++支持重载,因此g++编译后的函数名有额外信息,在gcc编译的c文件中无法识别符号,...
    99+
    2024-04-02
  • C++设计与实现ORM系统实例详解
    目录介绍依赖关系设计思路项目进度数据库通用接口实例构造智能查询方式设计单元测试运行方法介绍 我们通用的ORM,基本模式都是想要脱离数据库的,几乎都在编程语言层面建立模型,由程序去与数...
    99+
    2024-04-02
  • C++单例模式的几种实现方法详解
    目录局部静态变量方式静态成员变量指针方式智能指针方式辅助类智能指针单例模式通用的单例模板类总结局部静态变量方式 //通过静态成员变量实现单例 //懒汉式 class Single2 ...
    99+
    2024-04-02
  • C++实现单例模式日志输出详解
    目录一、单例模式简单介绍1.1 基础介绍1.2 单例模式使用场景二、单例模式实现日志记录(Singleton Logger)三、总结一、单例模式简单介绍 1.1 基础介绍 单例模式只...
    99+
    2023-05-17
    C++单例模式 日志输出 C++单例模式 C++ 日志输出
  • Java超详细讲解设计模式之一的单例模式
    目录单例模式1.单例模式的结构2.单例模式的实现2.1饿汉式2.2懒汉式3.单例模式的破坏3.1序列化和反序列化3.2反射单例模式 单例模式顾名思义就是单一的实例,涉及到一个单一的类...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作