<特殊类设计与单例模式>——《C++高阶》

news/2024/4/27 8:53:03/文章来源:https://blog.csdn.net/m0_57859086/article/details/127239882

目录

1.请设计一个类,不能被拷贝

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

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

4. 请设计一个类,不能被继承 

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

后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知


1.请设计一个类,不能被拷贝

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

将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
class CopyBan
{// ...private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};
原因:
1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不
能禁止拷贝了。
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写
反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
  • C++11

C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上
=delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{// ...CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;//...
};

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

实现方式:
1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
class HeapOnly    
{     
public:     static HeapOnly* CreateObject()  {      return new HeapOnly;    }
private:    HeapOnly() {}// C++98// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要// 2.声明成私有HeapOnly(const HeapOnly&);// or// C++11    HeapOnly(const HeapOnly&) = delete;
};

 

 

 

 

 

 

#include<iostream>
using namespace std;class HeapOnly
{
public://CreateObject是一个成员函数,要调用成员函数需要有对象//这里变为:要想创建对象就要调用这个函数,要想调用这个函数就要先有对象//即“先有鸡还是先有蛋?的问题”//那就可以将CreateObject成员函数设置成静态的//new 创建对象,会调用构造函数,而构造函数是成员函数中比较特殊的//普通的非静态成员函数需要使用对象去调用,而构造函数不需要//静态的使得this指针不能访问静态的成员,但可以访问静态构造函数//构造函数不用this指针去调用static HeapOnly* CreateObject(){return new HeapOnly;}
private://构造函数私有HeapOnly() {}//拷贝构造函数// C++98// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要// 2.声明成私有//HeapOnly(const HeapOnly&);// or// C++11    HeapOnly(const HeapOnly&) = delete;
};
int main()
{常用创建类对象方式//HeapOnly h1;                    //在局部//static HeapOnly h2;             //在静态区//HeapOnly* ph3 = new HeapOnly;   //在栈上HeapOnly* ph4 = HeapOnly::CreateObject();HeapOnly* ph5 = HeapOnly::CreateObject();//HeapOnly copy(*ph4);delete ph4;delete ph5;return 0;
}

 下面这种将析构函数私有的方式不常用,作为了解:

 

 

#include<iostream>
using namespace std;
class HeapOnly
{
public:HeapOnly(){}void DeleteObj(){delete this;}
private: //析构函数私有~HeapOnly() {}
};int main()
{//常用创建类对象方式//HeapOnly h1;                    //在局部//static HeapOnly h2;             //在静态区HeapOnly* ph3 = new HeapOnly;   //在栈上//delete ph3;ph3->DeleteObj();return 0;
} 

 

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

方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可。
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉// StackOnly obj = StackOnly::CreateObj();// StackOnly* ptr3 = new StackOnly(obj);void* operator new(size_t size) = delete;void operator delete(void* p) = delete;
private:StackOnly():_a(0){}
private:int _a;
};

 

 

#include<iostream>
using namespace std;
class StackOnly
{
public:static StackOnly CreateObj(){return  StackOnly();  //直接传值返回,编译器会优化,将:构造+拷贝构造——>构造/* StackOnly st;return st;*/}//StackOnly(const StackOnly&) = delete;void Print(){cout << "StackOnly" << endl;}
private://构造函数私有化StackOnly():_a(0){}int _a;
};int main()
{//常用创建类对象方式StackOnly h1=StackOnly::CreateObj();         //在局部StackOnly::CreateObj().Print();       //不使用对象接收//static StackOnly h2;                       //在静态区//StackOnly* ph3 = new StackOnly;            //在栈上return 0;
}

 

 

#include<iostream>
using namespace std;
class StackOnly
{
public:// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉// StackOnly obj = StackOnly::CreateObj();// StackOnly* ptr3 = new StackOnly(obj);void* operator new(size_t size) = delete;};
StackOnly st;                        //全局的也没有禁掉
int main()
{//StackOnly* p1 = new StackOnly;   //禁掉了newStackOnly s1;                    static StackOnly s2;             //静态的没有禁掉return 0;
}

 

 

 

 

4. 请设计一个类,不能被继承 

  • C++98方式
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};
  • C++11方法
fifinal关键字,fifinal修饰类,表示该类不能被继承。
class A  final
{// ....
};

 

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

设计模式:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式:
  • 饿汉模式
就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
// 饿汉模式
// 优点:简单
// 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
class Singleton
{
public:static Singleton* GetInstance(){return &m_instance;}
private:// 构造函数私有Singleton() {};// C++98 防拷贝Singleton(Singleton const&);Singleton& operator=(Singleton const&);// or// C++11Singleton(Singleton const&) = delete;Singleton& operator=(Singleton const&) = delete;static Singleton m_instance;
};Singleton Singleton::m_instance;  // 在程序入口之前就完成单例对象的初始化
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
  • 懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取
文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,
就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

 

// 懒汉
// 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
// 缺点:复杂
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Singleton
{
public:static Singleton* GetInstance() {// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全if (nullptr == m_pInstance) {m_mtx.lock();if (nullptr == m_pInstance) {m_pInstance = new Singleton();}m_mtx.unlock();}return m_pInstance;}// 实现一个内嵌垃圾回收类    class CGarbo {public:~CGarbo() {if (Singleton::m_pInstance)delete Singleton::m_pInstance;}};// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象static CGarbo Garbo;
private:// 构造函数私有Singleton() {};// 防拷贝Singleton(Singleton const&);Singleton& operator=(Singleton const&);static Singleton* m_pInstance; // 单例对象指针static mutex m_mtx;   //互斥锁
};
Singleton* Singleton::m_pInstance = nullptr;
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;
int main()
{thread t1([] {cout << &Singleton::GetInstance() << endl; });thread t2([] {cout << &Singleton::GetInstance() << endl; });t1.join();t2.join();cout << &Singleton::GetInstance() << endl;cout << &Singleton::GetInstance() << endl;return 0;
}

 

 

 

 

 

 

 

 

#include<iostream>
#include<string>
using namespace std;class InforMgr
{
public:static InforMgr* GetInstance(){return _spInst;}
private:InforMgr(){}InforMgr(const InforMgr&) = delete;string _address;int _secretKey;static InforMgr* _spInst;   //声明
};InforMgr* InforMgr::_spInst = new InforMgr;
int main()
{InforMgr s1;static InforMgr s2;InforMgr* s3 =new InforMgr;InforMgr::GetInstance();return 0;
}

 

 

 

#include<iostream>
#include<string>
using namespace std;//InforMgr 单例模式
//懒汉模式——一开始不创建对象,第一次调用GetInstance再创建对象
class InforMgr
{
public:static InforMgr* GetInstance(){//多线程同时调用,还需要加锁if (_spInst == nullptr){_spInst  = new InforMgr;}return  _spInst;}void SetAddress(const string& s){_address = s;}string& GetAddress(){return _address;}
private:InforMgr(){}InforMgr(const InforMgr&) = delete;string _address;int _secretKey;static InforMgr* _spInst;   //声明
};InforMgr* InforMgr::_spInst = new InforMgr;
int main()
{/*InforMgr s1;static InforMgr s2;InforMgr* s3 = new InforMgr;*///全局只有一个InfoMgr对象InforMgr::GetInstance()->SetAddress("中国");cout << InforMgr::GetInstance()->GetInstance() << endl;return 0;
}

总结:

举个例子:

设计关于SQLMgr,CacheMgr 的单例,

  • 饿汉模式:使用简单,初始化顺序不确定,如果有依赖关系就会有问题,饿汉对象初始化慢且多个饿汉单例模式同时进行会影响程序启动。
  • 懒汉模式 :使用复杂,第一次调用时初始化,可以控制初始化顺序,延迟加载初始化,不影响程序启动。

后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知

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

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

相关文章

GD32F307VC+WIN10+VSCODE+GCC+JLINK环境build

为了构建Cortex M系列单片机免费开源的开发环境&#xff0c;网络上了解来看VSCODEGCCJLINK是一套比较高效的组合方式&#xff0c;下面记录环境搭建的流程。 我这边的PC环境为 WIN10专业版64bit。 工具准备 1. arm-none-eabi-gcc下载及安装 官网下载链接&#xff1a;Downloa…

c++数据结构:数组和向量

线性表&#xff1a; 在数据元素的非空有限集中 存在唯一的一个被叫做“第一个”的数据元素存在唯一的一个被叫做“最后一个”的数据元素除第一个之外&#xff0c;集合中的每个数据元素均只有一个前驱除最后一个之外&#xff0c;每个集合元素均只有一个后继数据结构中线性结构指…

文字识别检测入门(1)

CTPN 优点&#xff1a;对水平文字检测效果超级好 缺点&#xff1a;对扭曲的文字不好 RRPN 在faster的基础上改进 RPN改为RRPN ROI pooling改进为RROI pooling 能解决旋转&#xff0c;但是解决不了弯曲的曲面问题 EAST Anchor free 特征合并&#xff0c;检测不同尺度文本 检测各…

刷爆leetcode第三期 0007~0010

刷爆leetcode第三期 0007~0010 题目一 反转链表解法一解法二题目二 链表的中间节点题目三 链表的倒数第K个节点题目四 合并两个有序链表题目一 反转链表 解法一 给定单链表的头节点 head &#xff0c;请反转链表&#xff0c;并返回反转后的链表的头节点。 示例 1&#xff1a…

python与人工智能:线性回归和逻辑回归

线性回归 线性回归是利用数理统计中回归分析&#xff0c;来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法&#xff0c;运用 十分广泛。梯度下降&#xff1f; 梯度下降法的基本思想可以类比为一个下山的过程。 假设这样一个场景&#xff1a;一个人被困在山上&a…

零拷贝总结

数据交互模式 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dqLJVb5U-1665401648551)(en-resource://database/1074:1)] 读数据过程&#xff1a; 应用程序要读取磁盘数据&#xff0c;调用 read()函数从而实现用户态切换内核态&#xff0c;这是第 …

论文/机器学习笔记:SENet (Squeeze-and-Excitation Networks)

Image 2017 挑战赛夺冠paper 1 motivation 希望显式地建模特征通道&#xff08;channel&#xff09;之间的相互依赖关系 通过学习的方式来自动获取到每个特征通道的重要程度依照这个重要程度去提升有用的特征并抑制对当前任务用处不大的特征 2 模型 给定一个输入 x&#xff…

利用phpstudy导入mysql文件

1.创建mysql文件 mysql 常用命令&#xff1a; 打开mysql: mysql -u root -p 查看数据库&#xff1a; show databases; 创建 数据库: create database baseName (数据库名称) 使用数据库&#xff1a; use baseName(数据库名称) 显示表&#xff1a;show tables; 创建表&#xff…

C#【高级篇】 IntPtr是什么?怎么用?

C#学习汇总 - 总目录 C#【高级篇】 IntPtr是什么&#xff1f;怎么用&#xff1f;前言一、IntPtr&#xff08;IntPointer&#xff09;的由来二、IntPtr&#xff08;属于结构体&#xff09;的说明三、IntPtr的使用示例1、int类型与IntPtr类型之间的转换2、string类型与IntPtr之间…

Java collection集合的体系特点

Java collection集合的体系特点 集合类体系结构 collection单列集合&#xff0c;每个元素&#xff08;数据&#xff09;只包含一个值。 Map双列集合&#xff0c;每个元素包含两个值&#xff08;键值对&#xff09;。 collection集合体系&#xff08;常见&#xff09; colle…

位段是什么玩意?你听说过吗??

当我们学完结构体之后&#xff0c;我们就要好好学学结构体实现位段的能力&#xff01;&#xff01;&#xff01; 目录 一、位段是什么&#xff1f; 二、位段的内存分配 三、位段的跨平台问题 总结 一、位段是什么&#xff1f; 位段的声明和结构体大体相同&#xff0c;但是有两…

【Unity_AssetBundle】(二)AB包资源打包、Asset Bundle Browser工具的使用

1.Asset Bundle Browser包安装 Asset Bundle Browser是AB包打包工具较底版本Unity编辑器&#xff1a; Windows——>Package Mannger——>搜索Asset Bundle Browser进行下载导入即可 较高版本Unity编辑器&#xff1a; 高版本Unity在Addressables功能中封装了AB包功能 安…

Telnet、DHCP、静态路由、等价路由、环回接口、浮动静态路由详解

文章目录前言一、Telnet二、DHCP----动态主机配置协议手工配置缺陷报文类型DHCP租期地址池DHCP中继代理路由信息来源直连路由静态路由优先级数据流量是双向的静态路由的扩展配置等价路由环回接口手工汇总路由黑洞缺省路由空接口路由浮动静态路由前言 一、Telnet Telnet是位于…

【数据结构】 归并排序、 基数排序

目录 一、什么是归并排序&#xff1f; 二、归并排序 三、什么是基数排序&#xff1f; 四、基数排序 五、各种排序的比较 一、什么是归并排序&#xff1f; 归并排序是建立在归并操作上的一种有效&#xff0c;稳定的排序算法。是将已有序的子序列合并&#xff0c;得到完全有…

09-Pawn类 UE4 C++

1.首先创建一个C的Pawn类 右键点击Public&#xff0c;选择新建C类 选择Pawn&#xff0c;然后点击下一步 命名后&#xff0c;点击创建 创建完毕&#xff0c;双击打开MyPawn 2.在MyPawn.h中添加如下代码&#xff1a; UPROPERTY(EditAnywhere) class UStaticMeshComponent* Mes…

SpringCloud-31-Spring Cloud Config微服务与配置文件解耦

11.8 微服务与配置文件解耦 我们可以将之前的子模块中的配置提取出来&#xff0c;托管到gitee上统一管理&#xff0c;这样运维人员维护配置文件就不变动子模块了&#xff0c;实现了模块与配置的解耦。 下面用例子来解释下这种做法的好处 在基础工程spring-cloud-microservice…

热血江湖服务端架设开服搭建教程

热血江湖服务端架设开服搭建教程 玩网游比较多的小伙伴&#xff0c;相信对热血江湖这款游戏也不陌生&#xff0c;摆脱了传统武侠游戏阴暗血腥的游戏风格&#xff0c;提倡一种“明朗而愉快的武侠”精神。画面上即不会太随意又不会过于沉重&#xff0c;画面干净清新。活泼可爱的…

ORACLE新增数据库(用户),使用navicate

oracle新增数据库并不像mysql直接指令就行&#xff0c;百度一圈都是用Oracle Database Configuration Assistant的&#xff0c;其实navicate就直接可以新建&#xff0c;以下是新建方法&#xff1a; 1.连接数据库 2.新建表空间 点击navicate上方工具栏中"其它"&…

何为功能平价?特斯拉「抛弃」多传感融合,背后有哪些门道

技术与成本&#xff0c;永远是博弈的两方。 当大部分车企都在寻求通过增加更多、更高性能的传感器&#xff08;也就是通常所说的多传感融合技术&#xff09;来强化智能驾驶功能可靠性和拓展性的大背景下&#xff0c;特斯拉依然我行我素&#xff0c;继续沿着纯视觉感知的路线前…

盘点一个Python列表(元素多样)处理的实战题目(使用正则表达式也可以实现)

大家好,我是Python进阶者。 一、前言 前几天在Python白银交流群【凡人不烦人】问了一个Python列表处理的问题,提问截图如下:下面是他的部分数据: lst = [(问答题)(2) 假设镀锌钢管, http://admintk.sc.zzstep.com/UpLoadImage/2019-10-10/a84f340e-6c67-42b1-8eae-3dc14281…