【关于C++中----特殊类设计和单例模式】

文章目录

  • 一、设计一个类,不能被拷贝
    • 1.1C++98的实现方法及其弊端
    • 1.2 C++11的实现方法
  • 二、设计一个类,只能在堆上创建对象
  • 三、设计一个类,只能在栈上创建对象
  • 四、设计一个类,不能被继承
  • 五、设计一个类,只能创建一个对象(单例模式)
    • 5.1单例概念
    • 5.2饿汉模式
    • 5.3懒汉模式


一、设计一个类,不能被拷贝

1.1C++98的实现方法及其弊端

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

对此,C++98的实现方式是==将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有。==如下:

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

这样做的原因是:

  1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不
    能禁止拷贝了
  2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写
    反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

但是,这样设计也有一些弊端。

首先,类的用户可能不会意识到该类禁止拷贝操作,从而可能会在使用时出现错误
其次,由于编译器自动生成的拷贝构造函数和拷贝赋值运算符被禁止使用,所以如果需要在代码中执行拷贝操作,就必须手动编写对应的移动构造函数和移动赋值运算符,这可能增加代码的复杂性。

1.2 C++11的实现方法

C++11及其后续版本引入了更好的解决方案,即通过将拷贝构造函数和拷贝赋值运算符声明为deleted,来明确禁止拷贝操作。此外,C++11还引入了移动构造函数和移动赋值运算符,使得对特殊类的处理更加简便和安全。如下:

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

这样,当禁用拷贝构造函数和拷贝赋值运算符时,编译器会在对应的调用上产生一个编译错误。这样做可以有效阻止对该类的对象进行拷贝操作


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

为了实现这个要求,首先要将构造函数放在私有成员中,防止外界随便创建对象;其次,再增加一个公有成员函数,用来专门在堆上创建一个对象并返回。如下:

class OnlyOnHeap
{
public:OnlyOnHeap* CreateObj(){return new OnlyOnHeap;}
private:OnlyOnHeap(){}
};

但是这样,在外部调用CreateObj时,却是行不通的。
原因在于,要想调用公有成员函数,首先要有一个对象。但是要创建一个对象,目前的情况来看只能通过这个函数调用。彼此矛盾。

所以,为了解决这个问题,可以将成员函数变为静态的,这样它就没有this指针了,也就可以在外部直接调用了。

如下:

class OnlyOnHeap
{
public:static OnlyOnHeap* CreateObj(){return new OnlyOnHeap;}
private:OnlyOnHeap(){}
};int main()
{OnlyOnHeap* tmp = OnlyOnHeap::CreateObj();return 0;
}

但是这样还是存在漏洞,当用户在外部按照上述方法创建了一个在堆上的对象之后,可以直接对其解引用,然后会自动调用它自动生成的拷贝构造,这样就会创造出一个在栈上的对象。

所以,为了避免上述问题,还要禁止使用它的拷贝构造函数,如下:

class OnlyOnHeap
{
public:static OnlyOnHeap* CreateObj(){return new OnlyOnHeap;}
private:OnlyOnHeap(){}OnlyOnHeap(const OnlyOnHeap&) = delete;
};

还有另外一种方法:把析构函数设为私有,把构造函数设为公有,如下:

class OnlyOnHeap
{
public:OnlyOnHeap(){}
private:~OnlyOnHeap(){}OnlyOnHeap(const OnlyOnHeap&) = delete;
};int main()
{OnlyOnHeap* tmp = new OnlyOnHeap;return 0;
}

但是这种方法,导致不能在外部使用delete释放对象,因为不能在外部调用析构函数。
所有,需要增加一个成员函数,用它来间接调用析构函数,如下:

class OnlyOnHeap
{
public:OnlyOnHeap(){}void Destroy(){this->~OnlyOnHeap();}
private:~OnlyOnHeap(){}OnlyOnHeap(const OnlyOnHeap&) = delete;
};int main()
{OnlyOnHeap* tmp = new OnlyOnHeap;OnlyOnHeap::Destroy();return 0;
}

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

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

class OnlyOnStack
{
public:static OnlyOnStack CreateObj(){return OnlyOnStack();}
private:OnlyOnStack(){}
};int main()
{OnlyOnStack obj = OnlyOnStack::CreateObj();return 0;
}

但是这种方法还是可以创建静态的对象的,所以这个要求的实现还是有缺陷的,做不到百分百的符合要求。


四、设计一个类,不能被继承

在C++98中,可以通过将类的构造函数声明为私有来防止其他类继承该类。由于派生类需要调用基类的构造函数来完成对象的构造,而私有构造函数无法在派生类中直接访问,因此无法创建继承自该类的对象。如下:

class CannotBeInherited 
{
private:CannotBeInherited() {} // 私有构造函数friend class SomeOtherClass; // 允许某些类访问私有构造函数
};

在C++11中,可以使用 ​final​关键字来声明一个类,表示该类不能被继承。如下:

class CannotBeInherited final 
{// 类定义
};

使用 ​final​关键字修饰类后,任何试图从此类派生的尝试都会导致编译错误。

需要注意的是,在C++11中还可以通过将基类的析构函数声明为虚函数,并将其设为纯虚函数(​= 0​),从而使得该类成为一个抽象类,无法直接实例化或继承。这种方式一般适用于需要通过派生类来实现多态性和覆盖虚函数的情况。如下:

class CannotBeInheritedAbstract 
{
public:virtual ~CannotBeInheritedAbstract() = 0;
};CannotBeInheritedAbstract::~CannotBeInheritedAbstract() {}

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

5.1单例概念

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

5.2饿汉模式

饿汉模式指的是一开始就创建对象。
既然要求只能有一个全局的对象,就毫无疑问要把构造函数设为私有
然后设置一个静态私有的对象,设置一个静态公有的成员函数,以便间接对对象进行操作。如下:

class InfoSingleton
{
public:static InfoSingleton& GetInstance(){return _sins;}void Insert(string name, int n){_info[name] = n;}void Print() const{for (auto e : _info){cout << e.first << ": " << e.second << endl;}}
private:InfoSingleton() {}map<string, int> _info;
private:static InfoSingleton _sins;
};InfoSingleton InfoSingleton::_sins;int main()
{InfoSingleton::GetInstance().Insert("张三", 100);InfoSingleton& info = InfoSingleton::GetInstance();info.Insert("李四", 200);info.Print();return 0;
}

结果如下:
在这里插入图片描述
但是,这并不是真正的单例,因为上述代码中还可以使用拷贝构造(默认生成)。所以,还需要用其他方法使拷贝构造禁止使用。如下:

class InfoSingleton
{
public:static InfoSingleton& GetInstance(){return _sins;}void Insert(string name, int n){_info[name] = n;}void Print() const{for (auto e : _info){cout << e.first << ": " << e.second << endl;}}
private:InfoSingleton() {}map<string, int> _info;InfoSingleton(const InfoSingleton& info) = delete;InfoSingleton& operator=(const InfoSingleton& info) = delete;
private:static InfoSingleton _sins;
};

饿汉模式的缺点:

  • 程序启动时间延长:因为在类加载时就创建了实例,所以会增加程序的启动时间。这可能在大型程序中产生明显的启动延迟,特别是当实例的初始化需要较长时间时。
  • 内存浪费:在整个程序执行期间,该实例一直存在于内存中,即使在一些情况下没有被使用。这可能导致内存的浪费,特别是如果该实例占用较大的资源或数据。
  • 懒加载无效:饿汉模式无法实现懒加载(延迟加载),即只在需要的时候才创建实例。如果实例的创建和初始化对资源消耗较大,但有些情况下并不需要使用该实例,就会造成资源浪费。
  • 不支持动态配置:饿汉模式在程序运行时无法动态地改变实例的创建和初始化逻辑。如果需要根据运行时的需求来动态配置实例,就无法满足这个需求。

5.3懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
跟饿汉模式不同在于,它不是一开始就创建对象,而是在第一次获取单例对象时创建对象。在main函数之后才会创建,不会影响启动速度。如下:

class InfoSingleton
{
public:static InfoSingleton& GetInstance(){//第一次获取单例对象的时候创建对象if (_sins == nullptr){_sins = new InfoSingleton;}return *_sins;}void Insert(string name, int n){_info[name] = n;}void Print() const{for (auto e : _info){cout << e.first << ": " << e.second << endl;}}
private:InfoSingleton() {}map<string, int> _info;InfoSingleton(const InfoSingleton& info) = delete;InfoSingleton& operator=(const InfoSingleton& info) = delete;
private:static InfoSingleton* _sins;
};

但是上面的代码还是存在线程安全的问题,当多个县城一起调用GetInstance时,会有风险。所以,应该对上面的代码加锁保证安全。如下:

class InfoSingleton
{
public:static InfoSingleton& GetInstance(){//双检查加锁,针对第一次创建对象,避免每次都加锁if (_sins == nullptr){//第一次获取单例对象的时候创建对象std::lock_guard<mutex> lock(_smtx);if (_sins == nullptr){_sins = new InfoSingleton;}}return *_sins;}// 实现一个内嵌垃圾回收类class CGarbo {public:CGarbo(){if (Singleton::m_pInstance)delete Singleton::m_pInstance;}};// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象static CGarbo Garbo;void Insert(string name, int n){_info[name] = n;}void Print() const{for (auto e : _info){cout << e.first << ": " << e.second << endl;}}
private:InfoSingleton() {}map<string, int> _info;InfoSingleton(const InfoSingleton& info) = delete;InfoSingleton& operator=(const InfoSingleton& info) = delete;
private:static InfoSingleton* _sins;static mutex _smtx;
};
InfoSingleton* InfoSingleton::_sins= nullptr;
InfoSingleton::CGarbo Garbo;
mutex InfoSingleton::_smtx;

本篇完!青山不改,绿水长流!

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

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

相关文章

Leecode316: 去除重复字母

下面这里使用有序map——TreeMap来实现Map接口&#xff0c;但是相对顺序是不能改变的&#xff01;这样会使得后面的跑到前面去&#xff0c;所以有问题 最简单的思想肯定是暴力思想&#xff0c;就是从前往后寻找&#xff0c;一旦遇到存在的情况就. 重点在于明确两点&#xff1a…

【Docker】了解和使用Docker

文章底部有投票活动&#xff0c;赶快参与进来吧&#x1f603; 相信大家在开发过程中都听说过 Docker 一词&#xff0c;至于 Docker 在开发中扮演的角色&#xff0c;估计好多人都说不上来&#xff0c;今天就让阿Q带大家一起揭开它神秘的面纱&#xff01; 文章目录 什么是容器&a…

dolphinscheduler伪分布式安装

1、上传安装包 2、安装 #解压 重命名 [rootdatacollection conf]# cd /opt/modules/ [rootdatacollection modules]# tar -zxf apache-dolphinscheduler-2.0.6-bin.tar.gz -C /opt/installs/ [rootdatacollection modules]# cd ../installs/ [rootdatacollection installs]# m…

【广州华锐互动】智慧交通3D可视化交互平台

智慧交通3D可视化交互平台由广州华锐互动开发&#xff0c;是一种基于现代科技的智能交通管理系统&#xff0c;它能够实现对车站内部人员和车辆的实时监控和管理。该平台采用了先进的三维可视化技术&#xff0c;将车站内部的结构和设备以立体、直观的方式呈现在用户面前&#xf…

LangChain大型语言模型(LLM)应用开发(四):QA over Documents

LangChain是一个基于大语言模型&#xff08;如ChatGPT&#xff09;用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口&#xff0c;可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互&#x…

springboot整合ehcache和redis实现多级缓存实战案例

一、概述 在实际的工作中&#xff0c;我们通常会使用多级缓存机制&#xff0c;将本地缓存和分布式缓存结合起来&#xff0c;从而提高系统性能和响应速度。本文通过springboot整合ehcache和redis实现多级缓存案例实战&#xff0c;从源码角度分析下多级缓存实现原理。 二、实战案…

赛效:如何将PDF文件免费转换成Word文档

1&#xff1a;在网页上打开wdashi&#xff0c;默认进入PDF转Word页面&#xff0c;点击中间的上传文件图标。 2&#xff1a;将PDF文件添加上去之后&#xff0c;点击右下角的“开始转换”。 3&#xff1a;稍等片刻转换成功后&#xff0c;点击绿色的“立即下载”按钮&#xff0c;将…

做私域选个微还是企微,哪个有优势?

做私域&#xff0c;你必须要有一个&#xff0c;引流新客户及留存老客户的地方。 于是&#xff0c;就有很多人讨论或者纠结&#xff1a;做私域&#xff0c;选择个人微信&#xff1f;还是企业微信&#xff1f; 让我们一起来看看个人微信和企业微信在功能和使用上有哪些区别&…

[SpringBoot]单点登录

关于单点登录 单点登录的基本实现思想&#xff1a; 当客户端提交登录请求时&#xff0c;服务器端在验证登录成功后&#xff0c;将生成此用户对应的JWT数据&#xff0c;并响应到客户端 客户端在后续的访问中&#xff0c;将自行携带JWT数据发起请求&#xff0c;通常&#xff0c…

一篇搞懂steam/csgo搬砖原理

接触csgo游戏搬砖项目三年了&#xff0c;也有在别的论坛交流心得。让我无语的是有些已经游戏搬砖差不多半年&#xff0c;却还告诉我没有赚到钱&#xff0c;又或者说时常到可出售的时候利润少的可怕&#xff0c;总是说这个行业说水太深了&#xff01;那么请你告诉我&#xff0c;…

快快快快快快快快快快排

作者简介&#xff1a;დ旧言~&#xff0c;目前大一&#xff0c;现在学习Java&#xff0c;c&#xff0c;Python等 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 望小伙伴们点赞&#x1f44d;收藏✨加关注哟&#x1f495;&#x1f495; C语言实现快排☺️ ℹ️…

Ceph 块存储系统 RBD 接口

-创建 Ceph 块存储系统 RBD 接口- 1、创建一个名为 rbd-demo 的专门用于 RBD 的存储池 ceph osd pool create rbd-demo 64 642、将存储池转换为 RBD 模式 ceph osd pool application enable rbd-demo rbd3、初始化存储池 rbd pool init -p rbd-demo # -p 等同于 --pool4、…

jenkins手把手教你从入门到放弃01-jenkins简介(详解)

一、简介 jenkins是一个可扩展的持续集成引擎。持续集成&#xff0c;也就是通常所说的CI&#xff08;Continues Integration&#xff09;&#xff0c;可以说是现代软件技术开发的基础。持续集成是一种软件开发实践&#xff0c; 即团队开发成员经常集成他们的工作&#xff0c;通…

STM32 Proteus仿真LCD12864火灾检测烟雾火焰温度报警器MQ2 -0064

STM32 Proteus仿真LCD12864火灾检测烟雾火焰温度报警器MQ2 -0064 Proteus仿真小实验&#xff1a; STM32 Proteus仿真LCD12864火灾检测烟雾火焰温度报警器MQ2 -0064 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机 LCD12864 液晶显示DS18B20 温度传感器多个按键电位…

单例模式:懒汉式和饿汉式

目录 懒汉模式和饿汉模式 区别 示例 懒汉模式线程不安全 懒汉模式线程安全 懒汉模式内部静态变量线程安全 饿汉式线程安全 指的是在系统生命周期内&#xff0c;只产生一个实例。 懒汉模式和饿汉模式 分为懒汉式和饿汉式 区别 创建时机和线程安全 线程安全&#xff1…

高时空分辨率、高精度一体化预测技术的风、光、水自动化预测技术的应用

第一章 预测平台讲解及安装 一、高精度气象预测基础理论介绍 综合气象观测数值模拟模式&#xff1b; 全球预测模式、中尺度数值模式&#xff1b; 二、自动化预测平台介绍 Linux系统 Crontab定时任务执行机制 Bash脚本自动化编程 硬件需求简介 软件系统安装 …

分享一个加载按钮动画

先看效果&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>加载动画按钮</title><script src"https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2…

flutter开发实战-卡片翻转动画效果Transform+IndexedStack+rotateAnimation

flutter开发实战-实现卡片翻转动画效果 之前开发中遇到了商品卡片翻转&#xff0c;商品正面是商品图片、商品名称&#xff1b;背面是商品价格&#xff0c;需要做卡片翻转动画。 动画实现即&#xff1a;在一段时间内&#xff0c;快速地多次改变UI外观&#xff1b;由于人眼会产生…

FL Studio是什么软件?FL Studio2023最新更新内容

FL Studio是什么软件 FL Studio是由比利时软件公司Image-Line开发的音乐制作软件&#xff0c;它拥有丰富的音效、合成器、采样器、鼓机等工具。FL Studio支持多种音频文件格式&#xff0c;包括MIDI、MP3、WAV、OGG等&#xff0c;可以帮助用户自由地进行音乐创作。 FL Studio界…

如何有效利用chatgpt?

如何有效地使用ChatGPT&#xff1f; 代码、诗歌、歌曲和短篇小说都可以由 ChatGPT 以特定的风格编写。您所需要的只是正确的问题和适当的提示。以下是有关如何有效使用ChatGPT的一些提示和想法&#xff1a; 头脑 风暴获取初稿解决编码问题尝试不同的提示格式查找标题寻求帮助…