特殊类设计与单例模式

news2024/11/13 9:25:09

特殊类设计与单例模式

  • 一、不能被拷贝的类
    • 1、介绍
    • 2、示例代码
  • 二、只能在堆上创建对象的类
    • 1、介绍
    • 2、示例代码
  • 三、只能在栈上创建对象的类
    • 1、介绍
    • 2、示例代码
  • 四、单例模式
    • 1、介绍
    • 2、设计模式
    • 3、懒汉式
      • (1)介绍
      • (2)示例代码1
      • (3)运行结果
      • (4)示例代码2
      • (5)说明
    • 4、饿汉式
      • (1)介绍
      • (2)示例代码

一、不能被拷贝的类

1、介绍

  • 在C++中,设计一个类以禁止拷贝通常是通过声明拷贝构造函数和拷贝赋值运算符为delete来完成的。这样做的目的是为了防止该类的对象被不当地复制,这在处理一些拥有独特资源(如文件句柄、网络连接等)的类中特别有用。
  • 尽管禁用了拷贝,但仍然可以移动一个NonCopyable对象(除非也禁用了移动构造函数和移动赋值运算符)。
  • 在很多情况下,保留移动语义是有益的,因为它允许在不涉及实际资源复制的情况下传递或返回对象。

2、示例代码

class NonCopyable 
{
public:
	// 默认构造函数  
	NonCopyable() = default;

	// 声明拷贝构造函数为delete,防止拷贝  
	NonCopyable(const NonCopyable&) = delete;

	// 声明拷贝赋值运算符为delete,防止赋值拷贝  
	NonCopyable& operator=(const NonCopyable&) = delete;

	// 默认析构函数,因为类中不包含任何需要显式清理的资源  
	~NonCopyable() = default;

	// 如果需要,还可以声明移动构造函数和移动赋值运算符  
	// NonCopyable(NonCopyable&&) = default;  
	// NonCopyable& operator=(NonCopyable&&) = default;  
	// 但由于通常禁用了拷贝,保留移动语义是合理的  
};

int main() 
{
	NonCopyable obj1;

	// 以下尝试将导致编译错误  
	// NonCopyable obj2 = obj1; // 拷贝构造函数被删除  
	// NonCopyable obj3;  
	// obj3 = obj1; // 拷贝赋值运算符被删除  

	return 0;
}

二、只能在堆上创建对象的类

1、介绍

  • 在C++中,设计一个类使其对象只能在堆上创建,可以通过将类的构造函数声明为private或protected,并提供一个静态成员函数来返回指向新创建对象的指针(通常使用new关键字)来实现。
  • 此外,还可以将析构函数声明为public(或protected,如果类有继承关系),但通常不需要将其设为private,因为这样做会阻止对象被正常销毁(尽管可以通过智能指针(参见智能指针(RAII))来间接管理)。

2、示例代码

class HeapOnly
{
public:
	static HeapOnly* CreateObject()
	{
		return new HeapOnly;
	}

	HeapOnly(const HeapOnly& hp) = delete;
	HeapOnly& operator=(const HeapOnly&) = delete;
private:
	HeapOnly()
	{
		cout << "HeapOnly()" << endl;
	}
};

int main()
{
	HeapOnly* hp3 = HeapOnly::CreateObject();
}

三、只能在栈上创建对象的类

1、介绍

  • 在C++中,设计一个类使其对象只能在栈上创建,通常的做法是限制该类的动态内存分配。然而,C++标准本身并不直接提供机制来禁止对象在堆上创建(即使用new操作符)。不过,可以通过一些技巧来使得在堆上创建对象变得不切实际或难以维护。
  • 一个常见的策略是将析构函数声明为私有(或受保护,如果打算有子类),并确保没有提供公共的拷贝构造函数、拷贝赋值运算符、移动构造函数或移动赋值运算符(除非它们也被设计为受限的)。然而,这仍然不会阻止用户在堆上创建对象,因为用户仍然可以调用类的私有构造函数(通过友元函数或模板特化等技巧)。
  • 但是,一个更实际的做法是使类在堆上创建时变得不安全或不合理。然而,这些方法都不是强制性的。在C++中,真正的只能在栈上创建的约束是不可能的,因为C++的设计哲学是给予程序员足够的自由。
  • 还有另一个办法,因为C++在堆上创建对象需要使用new进行操作,这时可以在类的内部重载new操作符。当使用new时,默认会优先使用类内部的重载函数,而将这个重载函数声明为删除,就能使得该类不能在堆上创建对象。

2、示例代码

class StackOnly
{
public:
	static StackOnly CreateObject()
	{
		StackOnly so;
		return so;
	}

	void* operator new(size_t) = delete;

	/*void* operator new(size_t sz)
	{
		cout << "void* operator new(size_t sz)" << endl;
		return malloc(sz);
	}*/
private:
	StackOnly()
	{
		cout << "StackOnly()" << endl;
	}
};

int main()
{
	StackOnly so1 = StackOnly::CreateObject();
	
	//StackOnly* so2 = new StackOnly(so1);
	//StackOnly* so3 = new StackOnly;

	return 0;
}

四、单例模式

1、介绍

  • 单例模式(Singleton Pattern)是一种常用的软件设计模式,它确保一个类仅有一个实例,并提供一个全局访问点来获取这个实例。
  • C++中的单例模式可以实现为懒汉式(Lazy Initialization)和饿汉式(Eager Initialization)两种形式。这两种形式的主要区别在于实例化的时机。
  • 在C++中实现单例模式,需要确保以下关键点:
  • 私有构造函数:防止外部通过new关键字直接创建类的实例。
  • 私有静态成员变量:存储类的唯一实例。
  • 公有静态方法:提供一个全局访问点来获取类的唯一实例。

2、设计模式

  • 设计模式(Design Patterns)是软件开发人员在软件设计过程中面临的一般问题的解决方案。它们不是可以直接使用的代码,而是描述在特定上下文中常见问题的最佳实践和设计方法的模板。使用设计模式可以帮助开发人员构建可重用、可维护、可理解和可扩展的软件系统。
  • 设计模式大致可以分为创建型模式、结构型模式和行为型模式。
  • 创建型模式(Creational Patterns):这类模式主要关注对象的创建过程。它们提供了在创建对象时隐藏创建逻辑的方式,使得系统更加灵活,易于扩展。而单例模式就属于创建型模式的一种。
  • 结构型模式(Structural Patterns):结构型模式关注于如何通过组合类与对象来获得更大的结构。它们提供了一种方式来组织软件中的对象,以便更灵活地设计不同的软件结构。
  • 行为型模式(Behavioral Patterns):行为型模式关注于对象之间的交互和职责的分配。它们定义了对象如何通信以及对象如何分配职责。

3、懒汉式

(1)介绍

  • 懒汉式(Lazy Initialization)单例模式在类的静态成员变量被首次使用时才创建实例。
  • 这种方式在资源密集型或实例化成本较高的场景中很有用,因为它延迟了实例的创建直到真正需要它的时候。但是,它必须确保在多线程环境下是线程安全的。

(2)示例代码1

class idler
{
public:
	static idler* GetInstance()
	{
		if (_pi == nullptr)
		{
			unique_lock<mutex> ul(_mtx);
			if(_pi == nullptr)
				_pi = new idler;
		}
		return _pi;
	}

	void AddElement(const string& key, const string& value)
	{
		_dict[key] = value;
	}

	void Print()
	{
		for (auto& e : _dict)
		{
			cout << e.first << ":" << e.second << endl;
		}
		cout << endl;
	}

	static void DeleteIdler()
	{
		if (_pi)
		{
			delete _pi;
			_pi = nullptr;		//最好置为空
			cout << "DeleteIdler(),此函数可进行持久化操作" << endl;
		}
	}
private:
	idler()
	{}

	~idler()
	{
		cout << "此处可进行持久化操作" << endl;
	}

	idler(const idler&) = delete;
	idler& operator=(const idler&) = delete;

	struct gc
	{
		~gc()
		{
			DeleteIdler();
		}
	};

	map<string, string> _dict;
	static idler* _pi;
	static gc _gc;
	static mutex _mtx;
};
idler *idler::_pi = nullptr;
idler::gc idler::_gc;
mutex idler::_mtx;


void TestIdler()
{
	idler::GetInstance()->AddElement("snow", "雪");
	idler::GetInstance()->AddElement("dragon", "龙");
	idler::GetInstance()->AddElement("snowdragon", "雪龙");

	idler::GetInstance()->Print();
}

(3)运行结果

在这里插入图片描述

(4)示例代码2

class idler2
{
public:
	static idler2& GetInstance()
	{
		static idler2 id2;
		return id2;
	}
private:
	idler2()
	{
		cout << "idler2()" << endl;
	}

	~idler2()
	{
		cout << "此处可进行持久化操作" << endl;
	}

	idler2(const idler2&) = delete;
	idler2& operator=(const idler2&) = delete;
};


void TestIdler2()
{
	idler2::GetInstance();
}

(5)说明

在上方代码中的GetInstance中,id2是局部的静态对象,在第一次调用时才进行初始化操作。

4、饿汉式

(1)介绍

  • 饿汉式(Eager Initialization)单例模式在程序启动时,即在类加载的时候就创建了类的实例。
  • 这种方式简化了实现,并且因为是静态初始化,所以自动是线程安全的(在C++11及以后的标准中,静态局部变量的初始化是线程安全的)。但是,它可能会在不需要实例的时候就创建了实例,即浪费资源。

(2)示例代码

class Singleton 
{  
public:  
    static Singleton& getInstance() 
    {  
        return instance;  
    }  
  
    void doSomething() 
    {  
        std::cout << "Doing something..." << std::endl;  
    }  
private:  
    static Singleton instance;  
  
    Singleton() {}  
  
    Singleton(const Singleton&) = delete;  
    Singleton& operator=(const Singleton&) = delete; 
};  
  
Singleton Singleton::instance;
  
int main() 
{  
    Singleton& s1 = Singleton::getInstance();  
    s1.doSomething();  
    return 0;  
}

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2116587.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android 12 SystemUI下拉状态栏禁止QuickQSPanel展开

1.概述 遇到需求&#xff0c;QuickQSPanel首次下拉后展示快捷功能模块以后就是显示QuickQSPanel&#xff0c;而不展开QSPanel&#xff0c;接下来要从下滑手势下拉出状态栏分析功能实现。也就是直接是展开状态。 2、涉及核心类 frameworks\base\packages\SystemUI\src\com\and…

STL经典案例(四)——实验室预约综合管理系统(项目涉及知识点很全面,内容有点多,耐心看完会有收获的!)

项目干货满满&#xff0c;内容有点过多&#xff0c;看起来可能会有点卡。系统提示读完超过俩小时&#xff0c;建议分多篇发布&#xff0c;我觉得分篇就不完整了&#xff0c;失去了这个项目的灵魂 一、需求分析 高校实验室预约管理系统包括三种不同身份&#xff1a;管理员、实…

【C++】手把手教你看懂的 STL map 详解(超详细解析,小白一看就懂!!)

目录 一、前言 二、预备知识 &#x1f4a2;关联式容器&#x1f4a2; &#x1f4a2;键值对&#x1f4a2; &#x1f4a2;哈希结构的关联式容器&#x1f4a2; 三、map 详解 &#x1f525;map 的介绍 &#x1f525;map的模板参数说明 &#x1f525;map的构造…

HarmonyOS应用开发( Beta5.0)HOS-用户认证服务:面部识别

介绍 User Authentication Kit&#xff08;用户认证服务&#xff09;提供了基于用户在设备本地注册的人脸和指纹来认证用户身份的能力。 用户向应用/系统服务请求访问某些个人数据或执行某些敏感操作时&#xff0c;应用/系统服务将调用系统用户身份认证控件对用户身份进行认证…

AI在医学领域:MIL回归用于前列腺癌复发预测

2024年&#xff0c;全球男性新癌症病例预计为1029080例&#xff0c;其中前列腺癌病例预计为29%。前列腺癌是男性中第二常见的癌症类型&#xff0c;仅次于肺癌。它主要影响老年男性&#xff0c;且发病率随年龄增长而增加。前列腺癌的主要治疗方法是前列腺切除术&#xff0c;但术…

知识竞赛答题软件应用场景有哪些

知识竞赛答题软件应用常见场景有哪些&#xff1f; 一、场景分析&#xff1a;该答题软件基于java技术和原生小程序开发完成&#xff0c;其功能主要包括&#xff1a;个人答题、好友pk、排位pk升级赛、专题pk答题、多人pk答题、积分兑换、排行榜等七大功能模块页面&#xff0c;适用…

记一次学习--内网穿透

目录 环境搭建 两张网卡如何配置 Ubuntu配置 渗透 ubuntu的拿下 centos的拿下 探测内网环境 fscan扫描 msf上马 渗透 拿下bage cms windows的拿下 ​编辑 使用fscan查看内网环境&#xff0c;发现了192.168.110.128这台设备 使用msf上马&#xff0c;现在这台机器是…

npm安装electron报错 RequestError: connect ETIMEDOUT 185.199.110.133:443

文章目录 npm安装electron报错的问题解决办法 npm安装electron报错的问题 报错信息如下&#xff1a; 由于网络原因一直报错&#xff0c;但是安装其他依赖没问题&#xff0c;查看源&#xff0c;使用淘宝源&#xff0c;也无效 解决办法 设置electron_mirror专用源: npm con…

C++入门基础知识57——【关于C++日期 时间】

成长路上不孤单&#x1f60a;【14后&#xff0c;C爱好者&#xff0c;持续分享所学&#xff0c;如有需要欢迎收藏转发&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#xff01;&#xff01;&#xff01;&#xff01;&#xff…

分布式部署①

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 1. 需要部署的服务 Nacos 理论上,应…

Popup源码分析 -- ant-design-vue系列

Popup源码分析 – ant-design-vue系列 1 极简代码 直接返回两个组件&#xff1a;Mask 和 PopupInner&#xff0c;后者在上一篇已经分析过了。下面我们先看一下 Mask的源码。 setup(props, { slots }) {return () > {if (!props.visible) return null;return (<div cla…

【Qt】窗口移动和大小改变事件

窗口移动和大小改变事件 moveEvent窗口移动时触发的事件resizeEvent窗口大小改变时触发的事件 例子&#xff1a;测试移动窗口和改变窗口事件 代码展示 #include "widget.h" #include "ui_widget.h"#include <QDebug> #include <QMoveEvent> …

chapter13-常用类——(String类)——day16

目录 477-StringBuffer方法 477-StringBuffer练习 479-StringBuilder结构剖析 480-StringBuilder应用 477-StringBuffer方法 三个字换两个字 477-StringBuffer练习 1、下面那个StringBuffer&#xff08;str&#xff09;有参构造器&#xff0c;在传入的是null的时候会报错&a…

mybatisplus使用OptimisticLockerInnerInterceptor实现版本号乐观锁

目录 OptimisticLockerInnerInterceptor 介绍 创建项目 创建项目 引入依赖 创建数据表 application.yml配置 项目结构 配置乐观锁拦截器 创建实体类 创建mapper 创建service 创建返回包装类BaseResponse 创建UserController 测试 查询 修改 ​编辑 修改后再查…

imu+wheel融合

ImuWheel融合 文章目录 ImuWheel融合1 轮速计1.1 航迹递推1.1.1 基于欧拉法1.1.2 基于二阶Runge-Kutta积分1.1.3 群空间闭式积分 1.2 雅可比计算 2 IMU观测更新3 数据处理 1 轮速计 1.1 航迹递推 ​ 常见的轮速计积分的方式有三种&#xff1a;欧拉积分、二阶Runge-Kutta积分、…

拯救者y9000p外接显示器黑屏

一开始会出现偶尔黑屏的情况&#xff0c;短则一两秒&#xff0c;长则五分钟。开始以为是屏幕或者是hdmi线的问题。后来网上查&#xff0c;发现可能是联想自带的XRite颜色校准器。 如果不需要该软件可以设置成为开机禁用&#xff0c;这样暂时就没问题了。

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣3217, 82, 237

总结&#xff0c;删除链表节点问题使用到列表&#xff0c;哈希表&#xff0c;递归比较容易超时&#xff0c;我觉得使用计数排序比较稳&#xff0c;处理起来也不是很难。 1. 力扣3217&#xff1a;从链表中移除在数组中的节点 1.1 题目&#xff1a; 给你一个整数数组 nums 和一…

LVM在Kubernetes下的最佳实践方案--TopoLVM

TopoLVM介绍及实践 LVM在Kubernetes下的最佳实践方案–TopoLVM。 1. 简介 TopoLVM 是一种基于 LVM&#xff08;Logical Volume Manager&#xff09;的 CSI&#xff08;Container Storage Interface&#xff09;插件&#xff0c;专为 Kubernetes 环境设计&#xff0c;旨在提供…

分布式部署②

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 对第四台服务器的补充 产品服务,订…

HTML 超链接

每一个网站都是由许多独立的网页组成&#xff0c;网页之家通常都是通过超链接来相互连接的。超链接可以让用户在各个独立的网页之间跳转。 <!DOCTYPE html> <html> <head><meta charset"utf-8" /><title>colspan属性</title>&l…