C++中特殊类设计/单例模式

特殊类设计

​ 本篇将会介绍一些特殊类的设计:不能拷贝的类、只能在堆上创建的类、只能在栈上创建的对象、不能被继承的类、只能创建一个对象的类(单例模式)。

文章目录

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

不能被拷贝的类

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

C++98写法:

​ 在C++98中还没有delete关键字,所以只能把拷贝构造和赋值运算符重载给放在private域中,这样类外对象就访问不到这两个对象,也就不能进行拷贝。

class CopyBan {
public:// ...
private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);// ...
};

C++11之后写法:

​ C++11可以直接使用delete关键字将这两个函数删除掉,如下:

class CopyBan {
public:// ...CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;
};
只能在堆上创建对象的类

​ 只能在堆上创建对象,也就意味着我们不能在类外直接声明定义,所以我们需要将构造函数私有化(不能禁止,还需要创建),同时还需要将拷贝构造函数禁止掉(或者私有化),因为可能通过拷贝构造在栈上创建对象。

​ 既然不能通过在类外定义,那么我们就需要在类内提供一个静态成员函数用于申请堆上的对象,如下:

class HeapOnly {
public:static HeapOnly* CreateObj() {return new HeapOnly;}// ...~HeapOnly() {// ...delete this;}
private:HeapOnly() {}HeapOnly(const HeapOnly&) {}// HeapOnly(const HeapOnly&) = delete;// ...
};

​ 同时还需要在析构函数中将自己给删除掉。

只能在栈上创建对象的类

​ 既然是只能在栈上创建的对象,那么我们就应该禁掉new和delete,但是new和delete是两个全局的关键字,我们可以在类内将new和delete重载,然后使用delete删除掉,这样对象不会被new出来了,如下:

// 第一种写法
class StackOnly {
public:void* operator new(size_t size) = delete;void operator delete(void* p) = delete;// ...
};// 第二种写法
class StackOnly {
public:static StackOnly CreateObj() {return StackOnly();}void* operator new(size_t size) = delete;void operator delete(void* p) = delete;// ...
private:StackOnly() {}
};
不能被继承

​ 只需要将类的构造函数给私有化,子类调用不到基类的构造函数,就不可以继承基类,如下:

class NonIherit {
public:static NonIherit GetInstance() {return NonIherit();}
private:NonIherit() {}
};// 也可以使用C++11中的关键字final
class NonIherit final {// ...
}
单例模式(只可以创建一个对象)

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

​ 单例模式一共有两种设计模式:

饿汉模式

​ 饿汉模式下的单例对象,在程序启动时就创建(进入main函数前),不管将来是否会使用到该单例对象,都会创建出来。如下:

class ConfigInfo {
public:static ConfigInfo* GetInstance() {return &_sinfo;}// 删除拷贝构造和赋值重载ConfigInfo(const ConfigInfo&) = delete;ConfigInfo& operator=(const ConfigInfo&) = delete;// ...
private:// 构造函数私有化ConfigInfo() {}std::string _ip;uint16_t _port;// ...// 静态变量,在类内声明static ConfigInfo _sinfo;
};// 类外定义
ConfigInfo ConfigInfo::_sinfo;

​ 如上所示,我们先将构造函数私有化,同时删除掉拷贝构造和赋值重载函数,接着在类内声明静态对象,在类外进行定义,这个时候就创建出了全局唯一的一个对象,我们可以使用GetInstance进行该对象访问。

​ 对于饿汉模式的优缺点如下:

优点:

​ 实现较为简单。

缺点:

​ 当单例对象较多的时候,会导致进程启动慢,因为各种单例对象在初始化的时候可能需要加载较多的资源。

​ 同时单例对象之间若存在互相依赖关系,将进一步导致效率降低,因为单例对象初始化的顺序不固定。

懒汉模式

​ 懒汉模式就是只有在需要使用该单例对象的时候才会加载该单例对象的资源,也就是一种延迟加载。实现如下:

class ConfigInfo {
public:// static ConfigInfo* GetInstance() {//     // 这种方式在C++11之前,多线程调用会存在线程安全问题//     // 可能会创建出多个单例对象//     // C++11对该问题进行了特殊处理//     static ConfigInfo info;//     return &info;// }static ConfigInfo* GetInstance() {if (_spinfo == nullptr) {// 判断两次_spinfo是否为nullptr是因为我们只需呀对// _spinfo变量初始化一次,也就是上锁初始化一次// 假若只判断_spinfo是否为nullptr,则每次都要加锁// 较为浪费效率std::unique_lock<std::mutex> lock(_mtx);if (_spinfo == nullptr) _spinfo = new ConfigInfo;}return _spinfo;}// 删除拷贝构造和赋值重载ConfigInfo(const ConfigInfo&) = delete;ConfigInfo& operator=(const ConfigInfo&) = delete;// ...void SetIp(const std::string& ip) {_ip = ip;}std::string GetIp() {return _ip;}
private:// 构造函数私有化ConfigInfo() {}std::string _ip;uint16_t _port;// ...static std::mutex _mtx;static ConfigInfo* _spinfo;
};std::mutex ConfigInfo::_mtx;
ConfigInfo* ConfigInfo::_spinfo = nullptr;

​ 如上所示,实现懒汉单例模式一共存在两种方式:

第一种:

​ 直接在GetInstance函数内定义一个静态的对象,每一次返回即可。这种方式实现得最为简单,不过这种方式在C++11之前会存在线程安全问题。C++11对这样的单例模式进行了特殊处理。

第二种:

​ 定义静态对象指针,以及锁,在类外定义该类和对象指针(两个变量基本没有消耗啥资源),然后在GetInstance函数中判断对象静态指针是否被初始化,没有初始化则new一个对象,已经初始化则直接返回即可。

​ 优缺点:

​ 优点:第一次使用实力对象的时候才创建对象,进程启动无负载。多个单例对象实力启动顺序可以控制。

​ 缺点:实现较为复杂。

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

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

相关文章

【代码大模型】Is Your Code Generated by ChatGPT Really Correct?论文阅读

Is Your Code Generated by ChatGPT Really Correct? Rigorous Evaluation of Large Language Models for Code Generation key word: evaluation framework, LLM-synthesized code, benchmark 论文&#xff1a;https://arxiv.org/pdf/2305.01210.pdf 代码&#xff1a;https:…

SpringBoot集成Dynamo(2)demo

一、dynamo local 1、建表 aws dynamodb create-table --table-name t_user --attribute-definitions AttributeNameuser_account,AttributeTypeS AttributeNameuser_name,AttributeTypeS --key-schema AttributeNameuser_account,KeyTypeHASH AttributeNameuser_name,KeyType…

Godot的开发框架应当是什么样子的?

目录 前言 全局协程还是实例协程&#xff1f; 存档&#xff01; 全局管理类&#xff1f; UI框架&#xff1f; Godot中的异步&#xff08;多线程&#xff09;加载 Godot中的ScriptableObject 游戏流程思考 结语 前言 这是一篇杂谈&#xff0c;主要内容是对我…

Spring Boot框架:电商系统的设计与实现

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本网上商城系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&…

tensorflow案例6--基于VGG16的猫狗识别(准确率99.8%+),以及tqdm、train_on_batch的简介

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 本次还是学习API和如何搭建神经网络为主&#xff0c;这一次用VGG16去对猫狗分类&#xff0c;效果还是很好的&#xff0c;达到了99.8% 文章目录 1、tqdm…

数字化转型企业架构设计手册(交付版),企业数字化转型建设思路、本质、数字化架构、数字化规划蓝图(PPT原件获取)

1、企业架构现状分析 2、企业架构内容框架 3、企业架构设计方法 3.1 、业务架构设计方法 3.2 、数据架构设计方法 3.3 、应用架构设计方法 3.4 、技术架构设计方法 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&…

Rust开发一个命令行工具(一,简单版持续更新)

依赖的包 cargo add clap --features derive clap命令行参数解析 项目目录 代码 main.rs mod utils;use clap::Parser; use utils::{editor::open_in_vscode,fs_tools::{file_exists, get_file, is_dir, list_dir, read_file}, }; /// 在文件中搜索模式并显示包含它的行。…

自动化运维(k8s):一键获取指定命名空间镜像包脚本

前言&#xff1a;脚本写成并非一蹴而就&#xff0c;需要不断的调式和修改&#xff0c;这里也是改到了7版本才在 生产环境 中验证成功。 该命令 和 脚本适用于以下场景&#xff1a;在某些项目中&#xff0c;由于特定的安全或政策要求&#xff0c;不允许连接到你的镜像仓库。然而…

Python学习笔记(1)装饰器、异常检测、标准库概览、面向对象

1 装饰器 装饰器&#xff08;decorators&#xff09;是 Python 中的一种高级功能&#xff0c;它允许你动态地修改函数或类的行为。 装饰器是一种函数&#xff0c;它接受一个函数作为参数&#xff0c;并返回一个新的函数或修改原来的函数。 语法使用 decorator_name 来应用在…

IntelliJ+SpringBoot项目实战(七)--在SpringBoot中整合Redis

Redis是项目开发中必不可少的缓存工具。所以在SpringBoot项目中必须整合Redis。下面是Redis整合的步骤&#xff1a; &#xff08;1&#xff09;因为目前使用openjweb-sys作为SpringBoot的启动应用&#xff0c;所以在openjweb-sys模块的application-dev.yml中增加配置参数&…

【UGUI】Unity 游戏开发:背包系统初始化道具教程

在游戏开发中&#xff0c;背包系统是一个非常常见的功能模块。它允许玩家收集、管理和使用各种道具。今天&#xff0c;我们将通过一个简单的示例来学习如何在 Unity 中初始化一个背包系统。我们将使用 Unity 2021.3.7 版本&#xff0c;并结合 C# 脚本来实现这一功能。 1. 场景…

AI工业大模型报告:体系架构、关键技术与典型应用

研究意义 随着新一代人工智能的发展, 大模型&#xff08;如 GPT-4o 等&#xff09;凭借大规模训练数据、网络参数和算 力涌现出强大的生成能力、泛化能力和自然交互能力, 展现出改变工业世界的巨大潜力. 尽管大模型 已在自然语言等多个领域取得突破性进展, 但其在工业应用中的…

电子电气架构 --- 电动汽车 800V 高压系统

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所有人的看法和评价都是暂时的&#xff0c;只有自己的经历是伴随一生的&#xff0c;几乎所有的担忧和畏惧…

shell编程之变量与引用

目录 深入认识变量什么是变量变量的名称变量数据类型变量的定义自定义变量环境变量位置变量 变量赋值和作用域赋值&#xff1a;变量名变量值read从键盘读入变量值变量和引号变量的作用域变量的运算 深入认识变量 什么是变量 变量是在程序中保存用户数据的一段内存存储空间&am…

UE5 材质里面画圆锯齿严重的问题

直接这么画圆会带来锯齿&#xff0c;我们对锯齿位置进行模糊 可以用smoothstep&#xff0c;做值的平滑过渡&#xff08;虽然不是模糊&#xff0c;但是类似&#xff09;

鸿蒙HarmonyOS开发:一次开发,多端部署(工程级)三层工程架构

文章目录 一、工程创建1、先创建出最基本的项目工程。2、新建common、features、 products 目录 二、工程结构三、依赖关系1、oh-package.json52、配置ohpm包依赖 四、引用ohpm包中的代码1、定义共享资源2、在common模块index文件中导出3、在phone模块oh-package.json5文件中引…

【笔记】关于git和GitHub和git bash

如何推送更新的代码到github仓库 如何在此项目已经提交在别的远程仓库的基础上更改远程仓库地址&#xff08;也就是换一个远程仓库提交&#xff09; 如何删除github中的一个文件 第二版 删除github上的一个仓库或者仓库里面的某个文件_github仓库删除一个文件好麻烦-CSDN博客 …

20241112-Pycharm使用托管的Anaconda的Jupyter Notebook

Pycharm使用托管的Anaconda的Jupyter Notebook 要求 不要每次使用 Pycharm 运行 Jupyter 文件时都要手动打开 Anaconda 的 Jupyter Notebook 正文 pycharm中配置好会自动安装的&#xff0c;有的要自己配置 Pycharm中配置 文件 ——> 设置 ——> 语言和框架……&am…

集合的介绍与比较器的应用

1.集合&#xff1a; 是一种容器&#xff0c;一种变量类型&#xff0c;跟数组很像 数组的缺点&#xff1a; A.数组的空间长度固定&#xff0c;一旦确定不可以更改。多了浪费&#xff0c;少了报错。 B.使用数组 操作数据的时候&#xff0c;【删除&#xff0c;增加】效率比较低。…

动态规划---解决多段图问题

ok 小伙伴们&#xff0c;我现在有点小小的红温&#xff0c;有点毛躁。 怎么解决多段图问题呢&#xff1f;求取最短路径有多种方法可取。 家人们&#xff0c;毫无思绪可言……………………………… 要实现动态规划&#xff0c;条件&#xff1a;子问题重叠度较高&#xff0c;并…