C++进阶 特殊类的设计

本篇博客介绍:介绍几种特殊的类

特殊类的设计

    • 设计一个类不能被拷贝
    • 设计一个类 只能在堆上创建对象
    • 设计一个类 只能在栈上创造对象
    • 设计一个类不能被继承
    • 单例模式
      • 饿汉模式
      • 懒汉模式
      • 单例模式对象的释放问题
    • 总结

设计一个类不能被拷贝

我们的拷贝只会发生在两个场景当中

  • 拷贝构造函数
  • 赋值运算符重载

所以说我们只需要让类失去 或者说不能使用这两个函数即可

这里有两个解决方案

在C++98中

我们将拷贝构造函数只声明不定义 并且将其访问权限设置为私有即可

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

原因如下

  • 我们设置为私有化之后 就能够禁止用户在外面调用 如果不设置私有 用户可以在外面定义并且调用
  • 因为我们不需要这个函数 所以定义没有意义 并且如果定义的话就可以在类内部调用从而以另一个函数完成拷贝

在C++11中

在C++11中 如果我们需要禁用一个函数直接使用delete关键字即可

代码标识如下

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

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

这个题目很有意思 让我们只能在堆上创建对象 也就是说我们不能在栈区还有静态区创建对象

再通俗一点说 我们不能创建局部变量和全局变量

用代码标识就是 下面的定义方式不允许

HeapOnly ho;
static HeapOnlu ho;

我们都知道 创建对象是需要构造函数的 所以说我们只需要将构造函数私有化之后 上面的创建方式就不允许了

但是实际上构造函数私有化之后我们也不能定义堆上的对象了

此时我们就需要创建一个新的函数 让这个函数帮助我们创建一个对象来

设计代码如下

  static HeapOnly* CreateObject() 
 {   
    return new HeapOnly;  
 }

我们使用一个静态函数new出来一个新的对象 然后返回这个对象的指针 当然我们也只有这种方式可以获取新的对象 也就是说我们只能在堆上创建对象了

与此同时我们要禁用拷贝构造和赋值运算符重载

因为可能会有人写出这样子的代码导致我们的对象创建在栈上

HeapOnly* HO2 = HeapOnly(HO1);

禁用拷贝构造和赋值运算符重载的思路同第一

设计一个类 只能在栈上创造对象

设计思路和只能在堆上创建对象类似 我们都是私有化构造函数之后暴露出一个静态函数来让外部调用

这个静态函数的唯一功能就是在栈上创建一个对象并且返回这个对象

class StackOnly
{
public:
	static StackOnly Createobj()
	{
		return StackOnly();
	}
private:
    //将构造函数设置为私有
	StackOnly()
    {}
};

这里有一点需要特别注意 因为我们是在函数的内部创建了一个对象 所以说这个对象是一个局部变量 所以说我们必须要使用传值返回而不能使用传引用返回

而传值返回不可避免的一点就是 我们需要使用拷贝构造函数

但是呢 有了拷贝构造函数之后我们就没办法限制在堆和静态区创建对象了 因为用户可以通过拷贝构造的方式来实现这一点

所以说我们一定没办法禁止全局对象的创建 即在静态区中创建对象

但是我们还是有办法禁止对象在堆上创建

因为new和delete的底层使用的是operator new和operator delete所以说我们只需要在类中禁用这两个成员函数即可

void* operator new(size_t size) = delete;
void operator delete(void* p) = delete;

设计一个类不能被继承

在C++98中

在C++98中 因为子类必须要调用父类的构造函数 所以说我们只需要将父类的构造函数私有化

此时父类就成为了事实上不能被继承的类了 代码标识如下

class NonInherit
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
    //构造函数私有
	NonInherit()
	{}
};

在C++11中

在C++11中 提供了一个关键字给我们使用 final

  • final关键字 如果我们在一个类的后面加上final 那么该类就是最终类 不能被继承
class A final
{
  //...  
};

单例模式

设计模式概念:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

使用设计模式的目的:
为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

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

单例模式有两种实现方式 分别是 饿汉模式懒汉模式

饿汉模式

饿汉模式我们可以理解为这个人十分的饥饿 所以说食物必须要提前准备好

反应到代码中 就是这个单例要在main函数启动前就要准备好

设计思路如下

  • 我们让这个类中有一个静态的类对象 只声明不定义
  • 将构造函数私有化 私有化之后外部就不能通过构造函数来创建对象
  • 删除拷贝构造和赋值运算符重载函数
  • 在类外面定义这个静态对象
  • 暴露出一个静态函数 返回这个静态对象的指针

至此外面的饿汉模式就设计完毕了

//单例模式:全局只有唯一对象
//饿汉模式:一开始就创建对象(main函数之前)
class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		return _sins;
	}
private:
	InfoSingleton()
	{}
    InfoSingleton(InfoSingleton& info) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
private:
	static InfoSingleton _sins;//声明
};
InfoSingleton InfoSingleton::_sins;//定义,属于类域::,可以调用构造函数

饿汉模式的优缺点

优点:

  • 实现方式简单
  • 没有线程安全相关问题

缺点:

  • 因为在main函数之前就要定义 可能会导致启动慢
  • 无法控制单例初始化顺序

懒汉模式

懒汉模式我们可以理解为这个人十分的懒 所以说不到最后一刻要交任务的时候绝对不会做事的

反应到代码中 就是我们需要使用这个单例的时候这个单例才会创建 也就是说在main函数里面创建

设计思路如下

  • 我们让这个类中有一个静态的类对象 只声明不定义
  • 将构造函数私有化 私有化之后外部就不能通过构造函数来创建对象
  • 删除拷贝构造和赋值运算符重载函数
  • 在类外面定义这个静态对象指针设置为空
  • 暴露出一个静态函数 如果说指针为空则我们构造并返回一个新的对象

至此我们的懒汉模式就设计完毕了

//懒汉模式
class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		//第一次获取单例对象的时候创建对象
		if (_psins == nullptr)
		{
            //第一次获取单例对象的时候创建对象
			_psins = new InfoSingleton;
		}
		return *_psins;
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton& info) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;

private:
	static InfoSingleton* _psins;
};
InfoSingleton* InfoSingleton::_psins=nullptr;

懒汉模式的优缺点

优点:

  • 可以主动控制定义顺序
  • 在main函数后面启动 不影响启动时间

缺点:

  • 有很严重的线程安全问题

如果有两个线程同时进入了GetInstance()函数内部就有可能发生线程安全问题 而导致产生两个或多个对象出来

我们这里推荐一种双检查加锁模式

代码标识如下

static InfoSingleton& GetInstance()
	{
		//第一次获取单例对象的时候创建对象
		if (_psins == nullptr)//对象new出来以后,避免每次都加锁的检查,提高性能
		{
			_smtx.lock();
			if (_psins == nullptr)//保证线程安全的检查且只new一次
			{
				_psins = new InfoSingleton;
			}
			_smtx.unlock();
		}
		return *_psins;
	}

首先我们检查下 对象指针是否为空 如果为空我们加锁 (主要是为了避免无脑先加锁带来的效率损失)

其次我们加锁 并且再次检查对象指针是否为空 (主要是为了线程安全问题)

如果为空我们创建对象之后解锁 如果不为空我们直接解锁

单例模式对象的释放问题

我们的单例模式对象创建之后一般会运行到程序结束 所以说一般不存在释放问题

如果说就非要释放的话我们可以创造一个函数来释放我们的对象

static void DelInstance()
{
	_mtx.lock();
	if (_inst != nullptr)
	{
		delete _inst;
		_inst = nullptr;
	}
	_mtx.unlock();
}

此外如果我们担心内存泄漏问题的话也可以使用智能指针来管理该对象

总结

在这里插入图片描述

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

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

相关文章

Flink状态和状态管理

1.什么是状态 官方定义:当前计算流程需要依赖到之前计算的结果,那么之前计算的结果就是状态。 这句话还是挺好理解的,状态不只存在于Flink,也存在生活的方方面面,比如看到一个认识的人,如何识别认识呢&am…

CSS中的calc()函数有什么作用?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ CSS中的calc()函数及其作用⭐ 作用⭐ 示例1. 动态计算宽度:2. 响应式布局:3. 自适应字体大小:4. 计算间距: ⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点…

Spring之bean的生命周期

目录 1.Bean的初始化过程 1.1代码详解 1.2思考 2.Bean的单例与多例选择 2.1论证单例与多例优缺点 2.2论证初始化时间点 2.3个例演示 Spring Bean的生命周期: 一、通过XML、Java annotation(注解)以及Java Configuration(配置类),等方式…

【令牌桶算法与漏桶算法】

💧 令牌桶算法与漏桶算法 \color{#FF1493}{令牌桶算法与漏桶算法} 令牌桶算法与漏桶算法💧 🌷 仰望天空,妳我亦是行人.✨ 🦄 个人主页——微风撞见云的博客🎐 🐳 《数据结构与算法》专…

每日一题——螺旋矩阵

题目 给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。 数据范围:0≤n,m≤10,矩阵中任意元素都满足 ∣val∣≤100 要求:空间复杂度 O(nm) ,时间复杂度 O(nm)…

李沐pytorch学习-卷积网络及其实现

一、卷积概述 1.1 基本定义 卷积计算过程如图1所示,即输入矩阵和核函数(filter)对应的位置相乘,然后相加得到输出对应位置的数。 图1. 卷积计算过程 该过程可以形象地从图2中展现。 图2. 二维卷积示意图 1.2 实现互相关运算的代…

01-关于new Object()的问题

美团面试题关于Object o = new Object()的几个问题。 1、对象在内存中的存储布局? 实例化一个对象,在堆区开辟一段空间。 堆区由markword、类型指针(class point)、实例数据、对齐组成。 markword:由8个字节组成。 类型指针(class point):就是指向某class文件的指针,…

【3Ds Max】弯曲命令的简单使用

简介 在3ds Max中,"弯曲"(Bend)是一种用于在平面或曲面上创建弯曲效果的建模命令。使用弯曲命令,您可以将对象沿特定轴向弯曲,从而创建出各种弯曲的几何形状。以下是使用3ds Max中的弯曲命令的基本步骤&…

spring源码分析bean的生命周期(下)

doGetBean()执行过程 createBean()执行过程 一、DependsOn注解 spring创建对象之前会判断类上是否加了DependsOn注解,加了会遍历然后会添加到一个map中,spring会先创建DependsOn注解指定的类 二、spring类加载器 在合并BeanDefinition,确定…

solidwords(5)

我们打算从上面画出总体,再从上面、侧面切除 最后成品

AI重新定义音视频生产力“新范式”

// 编者按:AIGC无疑是当下的热门话题和场景。面对AI带来的技术变革和算力挑战,该如何应对?LiveVideoStackCon 2023上海站邀请到了网心科技副总裁武磊为我们分享网心在面对AI应用场景和业务需求下的实践经验。 文/武磊 编辑/LiveVideoStack …

2022年国考行政执法卷-判断推理

去掉重复题 例题 例题 例题 例题 例题 例题 例题 例题 例题 例题 类比推理 例题 例题 例题 例题 例题 例题

mac上如何压缩视频大小?

mac上如何压缩视频大小?由于视频文件体积庞大,常常会占据我们设备的大量存储空间。通常情况下,我们选择删除视频以释放内存,但这将永久丢失它们。然而,有一种更好的方法可以在不删除视频的情况下减小内存占用&#xff…

Linux网络编程:Socket套接字编程(Server服务器 Client客户端)

文章目录: 一:定义和流程分析 1.定义 2.流程分析 3.网络字节序 二:相关函数 IP地址转换函数inet_pton inet_ntop(本地字节序 网络字节序) socket函数(创建一个套接字) bind函数(给socket绑定一个服务器地址结…

计算机竞赛 图像检索算法

文章目录 1 前言2 图像检索介绍(1) 无监督图像检索(2) 有监督图像检索 3 图像检索步骤4 应用实例5 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 图像检索算法 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐&#xff…

SpringCloud Gateway:status: 503 error: Service Unavailable

使用SpringCloud Gateway路由请求时,出现如下错误 yml配置如下: 可能的一种原因是:yml配置了gateway.discovery.locator.enabledtrue,此时gateway会使用负载均衡模式路由请求,但是SpringCloud Alibaba删除了Ribbon的…

【es6】中的Generator

Generator 一、Generator 是什么?1.1 与普通函数写法不一样,有两个不同 二、Generator 使用2.1 书写方法 三、yield语句3.1 yield和return3.2 注意事项3.3 yield*语句3.4 yield*应用 四、next方法4.1参数 总结 一、Generator 是什么? Genera…

优化GitHub网站访问慢的问题

方法一、修改host文件解决 大型网站服务器都不会是只有一台服务器,而是多台服务器组成的集群一起对外提供服务。 使用站长工具测速,找一个速度比较快的服务器。 图中可以看到140.82.121.4这个ip比较快, 下面修改hosts: Mac 在 /etc/hosts 中&#x…

Dubbo高手之路3,Dubbo服务消费详解

目录 引言1. 介绍 Dubbo 服务消费的详解的目的和背景2. 概述 Dubbo 服务消费的过程和核心概念 一、Dubbo 服务消费的基础知识1. Dubbo 服务消费的架构和流程2. Dubbo 服务消费的基本配置和使用方法 二、Dubbo 服务消费的注册与发现1. Dubbo 服务消费的注册中心和发布中心的基本…

帆软大屏2.0企业制作

 数字化观点中心 / 当前页 如何从0-1制作数据大屏,我用大白话给你解释清楚了 文 | 商业智能BI相关文章 阅读次数:18,192 次浏览 2023-06-08 11:51:49 好莱坞大片《摩天营救》中有这么一个场景:  你可以看见反派大b…
最新文章