【C++从0到王者】第四十一站:特殊类的设计

文章目录

  • 一、设计一个类,不能被拷贝
    • 1.C++98方法
    • 2.C++11方法
  • 二、设计一个类,只能在堆上创建对象
    • 1.析构函数私有化
    • 2.构造函数私有化
  • 三、请设计一个类,只能在栈上创建对象
  • 四、设计一个类不能被继承
    • 1.C++98方式
    • 2.C++11方式
  • 五、设计一个类,只能创建一个对象(单例模式)
    • 1.饿汉模式
    • 2.懒汉模式

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

这个我们前面已经说过了

有两种方法

1.C++98方法

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

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

原因:

  1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了

  2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

2.C++11方法

C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。

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

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

1.析构函数私有化

如下所示,由于在栈区和静态区的资源在生命周期结束的时候会调用析构函数。所以我们大可以直接将析构函数私有化。

这样一来,栈区和静态区的就无法创建对象了。而对于堆区的,由于他本身不会自动调用析构函数。我们需要手动去释放,但是由于我们现在的析构函数私有化了,所以我们可以通过一个接口去析构。如下代码所示:

class HeapOnly
{
public:void Destory(){delete this;}
private:~HeapOnly(){//...}
};
int main()
{HeapOnly hp1;static HeapOnly hp2;HeapOnly* hp3 = new HeapOnly;hp3->Destory();return 0;
}

我们可以明显的注意到,前两个是报错的

image-20240205013719829

2.构造函数私有化

这里需要注意的是,我们也要封住拷贝构造函数。因为可能会通过拷贝构造函数去创建栈区上的对象

class HeapOnly
{
public:static HeapOnly* CreatObj(){return new HeapOnly;}
private:HeapOnly(){//...}//C++11的方法,拷贝构造必须封HeapOnly(const HeapOnly& hp) = delete;//赋值运算符重载可封可不封HeapOnly& operator=(const HeapOnly& hp) = delete;
};
int main()
{HeapOnly hp1;static HeapOnly hp2;HeapOnly* hp3 = HeapOnly::CreatObj();//封住拷贝构造是为了防止下面的情形HeapOnly hp4(*hp3);return 0;
}

我们显然看到,我们这个只能在堆区创建了

image-20240205014636704

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

如下代码所示

为了让只在栈上创建对象,我们肯定不可以封住析构函数,因为栈区的一定会调用析构函数。

所以我们只能从构造函数下手,于是我们可以将构造函数私有化,然后提供一个接口去接收这个对象。

这里还需要注意的是,我们的new也可以是拷贝构造。但是我们是不可以封住拷贝构造的,因为我们返回一个对象的时候,需要调用拷贝构造。

我们注意到operator new是在全局中的一个函数重载,所以我们可以利用它会优先访问类域的特性,我们在类里面实现一个专属的operator new,然后我们就可以将这个函数给删掉。也就是无法使用new了。

最终我们就彻底屏蔽了堆区的创建。

但是这里我们其实还有一个静态区如果调用拷贝构造怎么办?这里如果还有屏蔽掉静态就比较麻烦了。虽然无法彻底解决问题,但是也已经可以了。

class StackOnly
{
public:static StackOnly CreatObj(){return StackOnly();}
private:StackOnly(){//...}//对一个类实现专属的operator newvoid* operator new(size_t size) = delete;
};int main()
{StackOnly st1;static StackOnly st2;StackOnly* st3 = new StackOnly;StackOnly st4 = StackOnly::CreatObj();StackOnly st5(st4);StackOnly* st6 = new StackOnly(st4);return 0;
}

我们可以注意到,确实只可以在栈上创建对象

image-20240205225248713

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

1.C++98方式

由于继承的派生类必须调用基类的构造函数。所以我们可以封住构造函数

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};

2.C++11方式

final关键字可以禁止某个虚函数无法被重写

final还可以修饰类,表示该类不能被继承。

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

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

1.饿汉模式

饿汉模式就是:一开始(main函数之前)就创建单例对象

问题:

  1. 如果单例对象很大或者初始化内容很多,影响启动速度
  2. 如果两个单例类,互相有依赖关系。要求A先创建,B再创建,B的初始化创建依赖A

饿汉模式的实现如下

要注意,为了防止拷贝构造或者赋值运算符重载去创建新的对象,要将他们给封住

class Singleyton
{
public:static Singleyton* GetInstance(){return &_sinst;}
private:Singleyton(){}//禁止拷贝Singleyton(const Singleyton& s) = delete;//禁止赋值Singleyton& operator=(const Singleyton& s) = delete;map<string, string> _dict;//可以放到静态区,注意这里只是声明static Singleyton _sinst;
};
//定义,这里的_sinst是在类里面声明的,所以可以调用类里面的构造函数
//就像一个函数在类里面声明,在外面定义是可以直接使用类里面的成员变量一样的
Singleyton Singleyton::_sinst;int main()
{Singleyton* s1 = Singleyton::GetInstance();Singleyton* s2 = Singleyton::GetInstance();Singleyton* s3 = Singleyton::GetInstance();Singleyton* s4 = Singleyton::GetInstance();cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s4 << endl;return 0;
}

image-20240205233101010

2.懒汉模式

我们先看下面的代码

懒汉模式其实就是一开始先不创建创建对象,而是在第一次去获取这个对象的时候去创建的。

对于单例模式它一般是不需要释放的。但是在一些特殊场景还是需要的。这时候我们需要显示释放,程序结束时,需要做一些持久化的动作(写到文件中去)

如下中,是写了一个Delnstance函数去释放这块资源的。

为了方便,我们可以专门去写一个类,定义一个全局对象去专门来释放它。

namespace lazy
{class Singleyton{public:static Singleyton* GetInstance(){//如果还没有创建,就创建一下这个对象if (_psinst == nullptr){_psinst = new Singleyton;}return _psinst;}//一般而言单例不用释放,//在一些特殊场景: 1. 需要显示释放, 2. 程序结束时,需要做一些特殊动作(如持久化)static void DelInstance(){if (_psinst){delete _psinst;_psinst = nullptr;}}~Singleyton() {cout << "~Singleyton()" << endl;//map的数据写到文件中FILE* fin = fopen("map.txt", "w");for (auto& e : _dict){fputs(e.first.c_str(), fin);fputs(":", fin);fputs(e.second.c_str(), fin);}}void ADD(string s1, string s2){_dict[s1] = s2;}void Print(){for (auto& e : _dict){cout << e.first << ":" << e.second << endl;}}private:Singleyton(){}//禁止拷贝Singleyton(const Singleyton& s) = delete;//禁止赋值Singleyton& operator=(const Singleyton& s) = delete;map<string, string> _dict;//可以放到静态区,注意这里只是声明static Singleyton* _psinst;};Singleyton* Singleyton::_psinst = nullptr;
}
class GC
{
public:~GC(){lazy::Singleyton::DelInstance();}
};
GC g;
int main()
{lazy::Singleyton* s1 = lazy::Singleyton::GetInstance();s1->ADD("xxx", "111");s1->ADD("yyy", "222");s1->ADD("zzz", "333");s1->ADD("abc", "444");s1->Print();//s1->DelInstance();return 0;
}

运行结果为

image-20240205235753609

image-20240206000233100

不过我们也可以将这个类写到内部类里面,这样的话这个也是可以的

namespace lazy
{class Singleyton{public:class GC{public:~GC(){lazy::Singleyton::DelInstance();}};static Singleyton* GetInstance(){//如果还没有创建,就创建一下这个对象if (_psinst == nullptr){_psinst = new Singleyton;}return _psinst;}//一般而言单例不用释放,//在一些特殊场景: 1. 需要显示释放, 2. 程序结束时,需要做一些特殊动作(如持久化)static void DelInstance(){if (_psinst){delete _psinst;_psinst = nullptr;}}~Singleyton() {cout << "~Singleyton()" << endl;//map的数据写到文件中FILE* fin = fopen("map.txt", "w");for (auto& e : _dict){fputs(e.first.c_str(), fin);fputs(":", fin);fputs(e.second.c_str(), fin);}}void ADD(string s1, string s2){_dict[s1] = s2;}void Print(){for (auto& e : _dict){cout << e.first << ":" << e.second << endl;}}private:Singleyton(){}//禁止拷贝Singleyton(const Singleyton& s) = delete;//禁止赋值Singleyton& operator=(const Singleyton& s) = delete;map<string, string> _dict;//可以放到静态区,注意这里只是声明static Singleyton* _psinst;static GC _gc;};Singleyton* Singleyton::_psinst = nullptr;Singleyton::GC Singleyton::_gc;
}int main()
{lazy::Singleyton* s1 = lazy::Singleyton::GetInstance();s1->ADD("xxx", "111");s1->ADD("yyy", "222");s1->ADD("zzz", "333");s1->ADD("abc", "444");s1->Print();//s1->DelInstance();return 0;
}

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

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

相关文章

【JavaScript 漫游】【013】Date 对象知识点摘录

文章简介 本文为【JavaScript 漫游】专栏的第 013 篇文章&#xff0c;记录了 JS 语言中 Date 对象的重要知识点。 普通函数的用法构造函数的用法日期的运算静态方法&#xff0c;包括&#xff1a;Date.now()、Date.parse() 和 Date.UTC()实例方法&#xff0c;包括&#xff1a;…

H5/CSS 笔试面试考题(31-40)

简述CSS 样式&#xff0c;边距&#xff1a; 10px 20px 40px 30px &#xff1b;哪一个是底边距 ( ) A&#xff1a;10px B&#xff1a;20px C&#xff1a;40px D&#xff1a;30px 面试通过率&#xff1a;45.0% 推荐指数&#xff1a; ★★★ 试题难度&#xff1a; 初级 试题类型&a…

JavaScript的原型与继承

原型 原型 prototype&#xff0c;我们所创建的每一个实例&#xff0c;解析器都会向这个函数中添加一个prototype&#xff0c;属性&#xff0c;这个属性会对应这个一个对象&#xff0c;这个对象就是原型对象&#xff08;显式原型&#xff09;&#xff0c;原型对象就相当于一个公…

156基于Matlab的光纤陀螺随机噪声和信号

基于Matlab的光纤陀螺随机噪声和信号&#xff0c;利用固定步长和可调步长的LMS自适应滤波、最小二乘法、滑动均值三种方法进行降噪处理&#xff0c;最后用阿兰方差评价降噪效果。程序已调通&#xff0c;可直接运行。 156 信号处理 自适应滤波 降噪效果评估 (xiaohongshu.com)

YOLOv5改进 | 融合改进篇 | 华为VanillaNet + BiFPN突破涨点极限

一、本文介绍 本文给大家带来的改进机制是华为VanillaNet主干配合BiFPN实现融合涨点,这个主干是一种注重极简主义和效率的神经网络我也将其进行了实验, 其中的BiFPN不用介绍了从其发布到现在一直是比较热门的改进机制,其主要思想是通过多层级的特征金字塔和双向信息传递来提…

[office] Excel自带的编辑函数求和方法 #其他#媒体

Excel自带的编辑函数求和方法 今天小编为大家分享Excel自带的编辑函数求和方法&#xff0c;方法很简单的&#xff0c;对于不是很熟悉excel表格的朋友可以参考一下&#xff0c;希望能对大家有所帮助 很多同学以及上班族需要大量使用Excel这款表格编辑器&#xff0c;当表格中有大…

企业IT故障应急响应:四大关键控制点的精细管理

面对不断复杂的生产环境&#xff0c;如何围绕“故障发现、故障响应、故障定位、故障恢复”四个关键环节&#xff0c;进行多方面统筹建设&#xff0c;从而达到增加TBF和缩短TTR的目标&#xff1f; TBF&#xff08;无故障时长&#xff09;和TTR&#xff08;故障修复时长&#xf…

论文笔记:相似感知的多模态假新闻检测

整理了RecSys2020 Progressive Layered Extraction : A Novel Multi-Task Learning Model for Personalized Recommendations&#xff09;论文的阅读笔记 背景模型实验 论文地址&#xff1a;SAFE 背景 在此之前&#xff0c;对利用新闻文章中文本信息和视觉信息之间的关系(相似…

Bert与ChatGPT

1. Bert模型 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一种预训练语言表示的方法&#xff0c;由Google AI在2018年提出。它标志着自然语言处理&#xff08;NLP&#xff09;领域的一个重大进步&#xff0c;因为它能够理解单词在…

CSP-202009-1-称检测点查询

CSP-202009-1-称检测点查询 解题思路 本题的时间复杂度貌似没有限制&#xff0c;直接暴力枚举就能知识盲点&#xff1a;sort()函数-升序排序 #include <algorithm>给名为dis&#xff0c;长度为n的数组排序sort(new_dis, new_dis n); #include <iostream> #inc…

第69讲后端登录逻辑实现

Admin实体&#xff1a; TableName("t_admin") Data public class Admin {TableId(type IdType.AUTO)private Integer id; // 编号private String userName; // 用户名private String password; // 密码TableField(select false)private String newPassword; // 新…

labelimg 在pycharm下载使用

labelimg 使用数据标注工具 labelimg 制作数据集 在pycharm中搜索labelimg 选择版本安装 labelimg install 使用数据标注工具制作数据集 启动 带参数启动 1、cmd cd到指定目录 2、带参数启动标注工具 左侧可以选择切换为需要的数据格式 一些快捷键 和自动保存&#xff0c…

Stable Diffusion 模型下载:RealCartoon-Realistic - V13

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十下载地址模型介绍 该检查点是 RealCartoon3D 检查点的一个分支。这个目标是在背景和人物中产生更“真实”的外观。我试图避免这个模型中更多的动漫、卡通和“完美”外观。这是一个肯

hexo 博客搭建以及踩雷总结

搭建时的坑 文章置顶 安装一下这个依赖 npm install hexo-generator-topindex --save然后再文章的上面设置 top: number&#xff0c;数字越大&#xff0c;权重越大&#xff0c;也就是越靠顶部 hexo 每次推送 nginx 都访问不到 宝塔自带的 nginx 的 config 里默认的角色是 …

python33-Python列表和元组之子序列

与前面介绍的字符串操作类似的是&#xff0c;列表和元组同样也可使用索引获取中间一段&#xff0c;这种用法被称为slice(分片或切片)。slice的完整语法格式如下: [start:end:step] 上面语法中start、end两个索引值都可使用正数或负数&#xff0c;其中负数表示从倒数开始。该语…

leetcode-4的幂

342. 4的幂 使用数学方法 如果一个数是4的幂次方&#xff0c;那么它一定可以表示为2的幂次方乘以4的幂次方。而2的幂次方在二进制表示中只有一个1&#xff0c;所以只需要判断n是否可以被4整除&#xff0c;并且n/4是否也是4的幂次方即可。 class Solution:def isPowerOfFour(…

easyx 随机火花生成器 视觉盛宴

作品介绍&#xff1a; 在数字化艺术的世界里&#xff0c;我们经常寻求模拟自然现象的方式&#xff0c;为观众带来沉浸式的体验。本作品“随机火花生成器”就是一个尝试&#xff0c;通过编程模拟了火花的随机生成和消散过程。 在这段代码中&#xff0c;我们使用了EasyX图形库&…

前端JavaScript篇之如何获得对象非原型链上的属性?

目录 如何获得对象非原型链上的属性&#xff1f; 如何获得对象非原型链上的属性&#xff1f; 要获取对象上非原型链上的属性&#xff0c;可以使用 hasOwnProperty() 方法。这个方法是 JavaScript 内置的对象方法&#xff0c;用于检查一个对象是否包含指定名称的属性&#xff0…

Python 3 中的 super()

Python super Python 的 super() 函数允许我们显式地引用父类。在继承的情况下&#xff0c;当我们想要调用父类函数时&#xff0c;它非常有用。 Python super 函数示例 首先&#xff0c;让我们看一下我们在 Python 继承教程中使用的以下代码。在该示例代码中&#xff0c;父类…

【论文阅读笔记】InstantID : Zero-shot Identity-Preserving Generation in Seconds

InstantID:秒级零样本身份保持生成 理解摘要Introduction贡献 Related WorkText-to-image Diffusion ModelsSubject-driven Image GenerationID Preserving Image Generation Method实验定性实验消融实验与先前方法的对比富有创意的更多任务新视角合成身份插值多身份区域控制合…