iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++单例设计模式详细讲解
  • 153
分享到

C++单例设计模式详细讲解

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

目录特殊类设计只能在堆上创建对象的类请设计一个类只能在栈上创建对象请设计一个类不能被拷贝请设计一个类不能被继承请设计一个类只能创建一个对象(单例模式)懒汉模式和饿汉模式的对比特殊类设

特殊类设计

只能在堆上创建对象的类

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

实现方式:

  • 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
  • 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
class test 
{
public:
	static test* GetObj() 
	{
		return new test();  //堆上申请并创建一个对象
	}
private:
	//构造函数私有
	test() { cout << "调用了构造函数" << endl; }
	//拷贝构造私有化,无法实例化对象
	test(const test& obj){};
};
void func1() 
{
	test* p = test::GetObj();  //通过调用静态函数获取对象的指针
}

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

方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可。

//只能在栈上创建对象
class test1 
{
public:
	static test1 GetObj()
	{
		return test1(); //栈上创建一个对象并返回
	}
	test1() { cout << "调用了构造函数" << endl; }
private:
	//拷贝构造私有化无法实例化对象
	test1(const test1& obj) {  }
};

方法二:屏蔽new,因为new在底层调用void* operator new(size_t size)函数(会在堆上开辟空间),我们只需要在类里面自定义定位new和delete就会不再new的时候调用全局的operator new和operator delete 最后该函数私有化,也就防止了在堆上创建对象,注意:也要防止定位new

class test02 
{
public:
	test02() 
	{ }
private:
	//自定义的定位new、定位delete
	void *operator new(size_t size) 
	{ 
		//代码....  
	}
	void operator delete(void *p)  
	{  
		//代码....
	}
};

请设计一个类不能被拷贝

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

c++98将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可

原因:

  • 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
  • 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
  • C++11的写法也可以在 默认成员函数后面加上delete,表示让编译器删除掉该默认成员函数
class CopyBan
{
 // ...
private:
 CopyBan(const CopyBan&);
 CopyBan& operator=(const CopyBan&);
 //...
};

请设计一个类不能被继承

C++98会对父类的构造函数私有化,但是这个方法并不够彻底,实际上是子类还是继承了父类的,只是没办法实例化对象,那么也就没有意义了

class test
{
private:
	test(){}
}

C++11方法

final关键字,final修饰类,表示该类不能被继承

class A final
{
 // ....
};

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

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

单例模式:

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

单例模式有两种实现模式:

饿汉模式、就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象,如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

实现方法:在类里面定义一个静态的成员指针obj,通过提供一个静态的成员函数获取该指针obj

class Singleton 
{
public:
	static Singleton* GetInstance()
	{
		return obj;
	}
private:
	Singleton() {}
	Singleton(const Singleton& obj) {}
	static Singleton* obj;
};
Singleton* Singleton::obj = new Singlet	on; 

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

懒汉模式:我们需要考虑到多线程安全隐患,和处理的返回值指针指向的永远都是同一个对象的指针,这样子才不会失去单例对象的性质,主要改变的是static Singleton* GetInstance();接口函数的设计

static Singleton* GetInstance()
{
	//如果只有一层的if判断的我们的程序就会存在一定的问题,
	//在多线程的场景下,多个线程都是比较自由的,可读可写、或者只读、只写
	//这样在一些场景下就会导致单例对象失去他本身的性质,
	//比如:两个线程 th1、th2,同时进入到了if语句中,假设th1会先执行。
	//为obj 申请了一个对象并返回它的指针,难么这个实例对象也就创建了
	//最后return obj,th1结束后(不确定结束时间),
	//th2又会再次new一个对象给obj,最后导致obj被赋值两次
	//整个过程梳理:
	//1、th1线程存在内存泄漏,因为obj被赋值两次,而第一次new出来的对象并没有被释放,那么就存在了内存泄漏
	//2、obj被赋值两次,失去单例对象性质
	if (!obj) 
	{
		if (!obj) 
		{
			obj = new Singleton();
		}
	}
	return obj;  //返回对象的指针
}

改善一:引入C++11线程库,加入互斥管理线程

static Singleton* GetInstance()
{
		//通过加锁,保证了线程安全
		m_mtx.lock();  //加锁
		//假设th1先拿到锁,那么th1就会先执行下面的语句,而th2就会等待,
		//obj就会指向new出来的单例对象,最后th1解锁完了return ,而th2
		//才刚拿到锁继续if判断的时候obj已经有值了就会跳过if继续往下执
		//行,然后解锁最终return ,整个过程中单例对象只有一份,
		//不存在二次赋值
		if (!obj) 
		{
			obj = new Singleton();
		}
		m_mtx.unlock();  //解锁
	return obj;  //返回对象的指针
}

进一步优化

因为第一次加锁解锁之后,处理了线程安全的问题,而往后的obj指针已经被初始化了,也就不需要再new一次,所以可以再最外层套上一层if判断,防止继续枷锁解锁,因为频繁的加锁解锁会导致线程不断的切入切出有上下文切换的开销

static Singleton* GetInstance()
{
	if(!obj)  
		//第一次判断保护线程安全,第二次obj已经有值,不需要执行{....}
		//优点:保证线程安全的同时完成了单例对象的初始化
	{
		m_mtx.lock();  //加锁
		if (!obj)   //保证单例对象只有一份,不会存在二次赋值
		{
			obj = new Singleton();
		}
		m_mtx.unlock();  //解锁
	}
	return obj;  //返回对象的指针
}

完善实现

//懒汉
class Singleton 
{
public:
	class CGarbo {
	public:
		~CGarbo() {
			if (obj)   //释放对象指针,并置空
			{
				delete obj;
				obj = nullptr;
			}
		}
	};
public:
	static Singleton* GetInstance()
	{
		//双重检查的好处是保护了线程安全,同时又提高了效率
		if (!obj) 
		{
			m_mtx.lock();  //加锁
			if (!obj) 
			{
				obj = new Singleton();
			}
			m_mtx.unlock();  //解锁
		}
		return obj;  //返回对象的指针
	}
private:
	Singleton()  {}
	Singleton(const Singleton& obj) {}
	void operator=(const Singleton& obj) {}
	static Singleton* obj; //声明对象指针
	static mutex m_mtx; //声明互斥锁
	static CGarbo Garbo;  //声明垃圾回收器对象
};
Singleton* Singleton::obj = nullptr;  //对象指针初始化
mutex Singleton::m_mtx;  //定义互斥锁
Singleton::CGarbo Garbo;  //定义垃圾回收器对象

懒汉模式和饿汉模式的对比

饿汉

优点:简单

缺点:1、如果单例对象构造函数工作比较多,会导致程序启动慢,迟迟进不了入口main函数

2、如果有多个单例对象,他们之间有初始化依赖关系,饿汉模式也会有问题。

比如有A和B两个单例类,要求A单例先初始化,B必须在A之后初始化。那么饿汉无法保证

这种场景下面用懒汉就可以,懒汉可以先调用A::GetInstance(),再调用B::GetInstance().

懒汉

优点:解决上面饿汉的缺点。因为他是第一次调用GetInstance时创建初始化单例对象

缺点:相对饿汉,复杂一点点。

迭代器模式、适配器模式

设计模式有兴趣的同学,可以下去再看看工厂模式、观察者模式等等

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

--结束END--

本文标题: C++单例设计模式详细讲解

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

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

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++单例设计模式详细讲解
    目录特殊类设计只能在堆上创建对象的类请设计一个类只能在栈上创建对象请设计一个类不能被拷贝请设计一个类不能被继承请设计一个类只能创建一个对象(单例模式)懒汉模式和饿汉模式的对比特殊类设...
    99+
    2022-11-13
  • Golang设计模式之单例模式详细讲解
    目录单例模式概念示例单例模式 单例是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。 单例拥有与全局变量相同的优缺点。 尽管它们非常有用, 但...
    99+
    2023-01-11
    Go单例模式 Go设计模式
  • Java超详细讲解设计模式之一的单例模式
    目录单例模式1.单例模式的结构2.单例模式的实现2.1饿汉式2.2懒汉式3.单例模式的破坏3.1序列化和反序列化3.2反射单例模式 单例模式顾名思义就是单一的实例,涉及到一个单一的类...
    99+
    2022-11-13
  • C++设计模式之单例模式详解
    目录单例模式:就是只有一个实例。单例模式又分为两种基本的情形:饿汉式和懒汉式如下是懒汉式单例类小结:继续看单例模式总结单例模式:就是只有一个实例。 singleton pattern...
    99+
    2022-11-12
  • Java 超详细讲解设计模式之原型模式讲解
    目录传统方式原型模式基本介绍原型模式在spring框架中源码分析深入讨论-浅讨论和深拷贝原型模式的注意事项和细节传统方式 克隆羊问题 现在有一只羊 tom,姓名为: tom,年龄为:...
    99+
    2022-11-13
  • Spring设计模式中代理模式详细讲解
    目录一、场景模拟二、提出问题三、代理模式1.静态代理2.动态代理一、场景模拟 ①声明接口 声明计算器接口Calculator,包含加减乘除的抽象方法 public interface...
    99+
    2023-01-03
    Spring代理模式 Spring设计模式
  • Golang设计模式之原型模式详细讲解
    目录原型模式概念示例原型模式 原型是一种创建型设计模式, 使你能够复制对象, 甚至是复杂对象, 而又无需使代码依赖它们所属的类。 所有的原型类都必须有一个通用的接口, 使得即使在对象...
    99+
    2023-01-11
    Go原型模式 Go设计模式
  • Golang设计模式中的桥接模式详细讲解
    目录桥接模式概念示例桥接模式 桥接是一种结构型设计模式, 可将业务逻辑或一个大类拆分为不同的层次结构, 从而能独立地进行开发。 层次结构中的第一层 (通常称为抽象部分) 将包含对第二...
    99+
    2023-01-11
    Go桥接模式 Go设计模式
  • Golang设计模式之适配器模式详细讲解
    目录适配器模式概念示例适配器模式 适配器是一种结构型设计模式, 它能使不兼容的对象能够相互合作。 适配器可担任两个对象间的封装器, 它会接收对于一个对象的调用, 并将其转换为另一个对...
    99+
    2023-01-11
    Go适配器模式 Go设计模式
  • Go设计模式之生成器模式详细讲解
    目录生成器模式概念示例生成器模式 生成器是一种创建型设计模式, 使你能够分步骤创建复杂对象。 与其他创建型模式不同, 生成器不要求产品拥有通用接口。 这使得用相同的创建过程生成不同的...
    99+
    2023-01-11
    Go设计模式 Go生成器模式
  • JAVA设计模式之单例模式详解
    目录前言一、单例模式是什么?二、懒汉式单例三、饿汉式单例四、双重校验锁总结前言 在之前的文章里已经介绍了设计模式以及设计原则的概念,接下来我们从单例模式入手深入学习几种常用的JAVA...
    99+
    2022-11-13
  • Android设计模式之单例模式详解
    单例模式一个类只有一个实例,并且可以全局访问使用应用场景如账户管理类,数据库操作类等(某个对象频繁被访问使用)常用方式饿汉式懒汉式同步加锁DCL双重加锁验证静态内部类枚举单例饿汉式加载类的同时立即进行初始化操作,对资源消耗很大public ...
    99+
    2023-05-30
    android 设计模式 单例模式
  • C++示例讲解观察者设计模式
    目录引言什么是观察者设计模式解决的问题编程思路知识小百科引言 用来解决两个不相关对象之间的一对一或者一对多的通信模型。 什么是观察者设计模式 观察者模式是一种对象行为模式。它定义对象...
    99+
    2022-12-26
    C++观察者设计模式 C++观察者模式 C++设计模式
  • C#设计模式之单例模式
    单例模式也是创建型模式的一种,也是23种设计模式中比较简单的一种。见名思意,在整个软件系统中,只有某个类型的一个对象,并且访问他的地方也只有一个,也就是只有一个全局对象访问点,这个实...
    99+
    2022-11-13
  • Java超详细讲解设计模式中的命令模式
    目录介绍实现个人理解:把一个类里的多个命令分离出来,每个类里放一个命令,实现解耦合,一个类只对应一个功能,在使用命令时由另一个类来统一管理所有命令。 缺点:如果功能多了就会导致创建的...
    99+
    2022-11-13
  • Golang设计模式中抽象工厂模式详细讲解
    目录抽象工厂模式概念示例抽象工厂模式 抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。 抽象工厂定义了用于创建不同产品的接口, 但将实际的创建工...
    99+
    2023-01-11
    Go抽象工厂模式 Go设计模式
  • Java设计模式之单例模式示例详解
    目录0.概述1.饿汉式1.1 饿汉式单例实现1.2 破坏单例的几种情况1.3 预防单例的破坏2.枚举饿汉式2.1 枚举单例实现2.2 破坏单例3.懒汉式4.双检锁懒汉式5.内部类懒汉...
    99+
    2022-11-12
  • Java超详细讲解设计模式之一的工厂模式
    目录工厂模式1.简单工厂1.1结构1.2实现1.3优缺点1.4扩展2.工厂方法2.1结构2.2实现2.3优缺点3.抽象工厂3.1结构3.2实现3.3优缺点4.模式扩展4.1实现工厂模...
    99+
    2022-11-13
  • Java结构型设计模式之桥接模式详细讲解
    目录桥接模式概述应用场景优缺点主要角色桥接模式的基本使用创建实现角色创建具体实现角色创建抽象角色创建修正抽象角色客户端调用桥接模式实现消息发送创建实现角色创建具体实现角色创建抽象角色...
    99+
    2022-11-13
  • C++设计与声明超详细讲解
    目录让接口被正确使用不易被误用宁以pass-by-reference-to-const替换pass-by-value必须返回对象时将成员变量声明为private以non-member...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作