C++中noncopyable不可拷贝类的使用

背景:项目中需要某个类不能被拷贝构造和赋值构造,下面举例说明该场景:

什么时候需要不可拷贝类

考虑一种情况,我们要实现一个含有动态数组成员的类,其中动态数组成员在构造函数中 new 出来,在析构函数中 delete 掉。比如说这样一个矩阵类:

template<typename _T>
class Matrix {
public:int w;int h;_T* data;// 构造函数Matrix(int _w, int _h): w(_w), h(_h){data = new  _T[w*h];}// 析构函数~Matrix() {delete [] data;}
}

上面的测试 1 中,我们先构造了 m1m2 两个 Matrix 实例,这意味着他们各自开辟了一块动态内存来存储矩阵数据。然后我们使用 =m2 拷贝给 m1,这时候 m1 的每个成员(whdata)都被各自使用 = 运算符拷贝为和 m2 相同的值。m1.data 是个指针,所以就和 m2.data 指向了同一块的内存。于是这里就会出现两个问题:其一, 发生拷贝前 m1.data 指向的动态内存区在拷贝后不再拥有指向它的有效指针,无法被释放,于是发生了内存泄露;其二,在 copy() 结束后,m1m2 被销毁,各自调用析构函数,由于他们的 data 指向同一块内存,于是发生了双重释放。

测试 2 中也有类似问题。当调用 copy(Matrix<_T> cpy) 时,形参 cpy 拷贝自实参,而 cpy 会在函数结束时销毁,cpy.data 指向的内存被释放,所以实参的矩阵数据也被销毁了——这显然是我们不愿意看见的。同样的,在返回时,ret 随着函数结束而销毁,返回值因为拷贝自 ret,所以其矩阵数据也被销毁了。

因此,对于像 Matrix 这样的类,我们不希望这种拷贝发生。一个解决办法是重载拷贝函数,每次拷贝就开辟新的动态内存:

Matrix<_T>& operator = (const Matrix<_T>& cpy) {w = cpy.w;h = cpy.h;delete []  data;data = new _T[w*h];memcpy(data, cpy.data, sizeof(_T)*w*h);return *this;
}Matrix(const Matrix<_T>& cpy):w(cpy.w), h(cpy.h) {data = new _T[w*h];memcpy(data, cpy.data, sizeof(_T)*w*h);
}

这样做也有不好的地方。频繁开辟动态内存,当数据量很大时(比如图像处理),对程序性能是有影响的。在接口设计的角度考虑,应该把这种拷贝操作以较明显的形式提供给用户,比如禁用等号拷贝,以直接的函数代替 = 操作:

void copyFrom(const Matrix<_T>& cpy) {w = cpy.w;h = cpy.h;delete []  data;data = new _T[w*h];memcpy(data, cpy.data, sizeof(_T)*w*h);

再禁用构造拷贝,只允许用户以引用传递的办法在自定义函数中使用 Matrix 类。

那么,如何禁止拷贝操作呢?

实现不可拷贝类

使用 boost::noncopyable

Boost 作为 C++ 万金油工具箱,在 <boost/noncopyable.hpp> 下提供了不可拷贝类的实现,使用起来也非常简单,让自己的类继承自 boost::noncopyable 即可:

class Matrix : boost::noncopyable
{// 类实现
}

声明拷贝函数为私有

如果不想用第三方库,自己实现呢?不妨先看一下 Boost 是怎么做的:

private:  // emphasize the following members are privatenoncopyable( const noncopyable& );noncopyable& operator=( const noncopyable& );

嗯,****直接把拷贝函数声明为私有****的不就等于禁用了么,so smart!于是:

template<typename _T>
class Matrix 
{
private:Matrix(const Matrix<_T>&);Matrix<_T>& operator = (const Matrix<_T>&);
}

C++ 11 下使用 delete 关键字

C++ 11 中为不可拷贝类提供了更简单的实现方法,使用 delete 关键字即可

template
class Matrix
{
public:
Matrix(const Matrix<_T>&) = *delete*;
Matrix<_T>& operator = (const Matrix<_T>&) = *delete*;

}

关于类似 Matrix 矩阵类的实现,更高级的做法是像智能指针一样封装其内部数据,用内部计数器来确定动态分配的成员是否要释放掉,不过这是另外一个问题了。

boost::noncopyable比较简单, 主要用于单例的情况.

**通常情况下, 要写一个单例类就要在类的声明把它们的构造函数, 赋值函数, 析构函数, 复制构造函数隐藏到private或者protected之中, 每个类都这么做麻烦**.

​ 有noncopyable类, 只要让单例类直接继承noncopyable.
​ class noncopyable的基本思想是把构造函数和析构函数设置protected权限,这样子类可以调用,但是外面的类不能调用,那么当子类需要定义构造函数的时候不至于通不过编译。但是最关键的是****noncopyable把复制构造函数和复制赋值函数做成了private****,这就意味着除非子类定义自己的copy构造和赋值函数,否则在子类没有定义的情况下,外面的调用者是不能够通过赋值和copy构造等手段来产生一个新的子类对象的。

private: // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );

 
#ifndef BOOST_NONCOPYABLE_HPP_INCLUDED
#define BOOST_NONCOPYABLE_HPP_INCLUDEDnamespace boost {//  Private copy constructor and copy assignment ensure classes derived from
//  class noncopyable cannot be copied.//  Contributed by Dave Abrahamsnamespace noncopyable_  // protection from unintended ADL
{class noncopyable{protected:noncopyable() {}~noncopyable() {}private:  // emphasize the following members are privatenoncopyable( const noncopyable& );const noncopyable& operator=( const noncopyable& );};
}typedef noncopyable_::noncopyable noncopyable;} // namespace boost#endif  // BOOST_NONCOPYABLE_HPP_INCLUDED
 
#include "tfun.h"class myclass: public boost::noncopyable
{
public:myclass(){};myclass(int i){};
};int main()
{myclass cl1();myclass cl2(1);// myclass cl3(cl1);    // error// myclass cl4(cl2);    // errorreturn 0;
}

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

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

相关文章

Confluence 快速安装教程

安装jdk yum install -y java-1.8.0-openjdk.x86_64 java -version 安装MySQL mkdir -p /data/mysql/data chmod 777 /data/mysql/datadocker rm -f mysql docker run -d --name mysql \-p 3306:3306 \-e MYSQL_ROOT_PASSWORDfingard1 \-v /data/mysql/data:/var/lib/mysql …

c语言:用转移表实现加减乘除的计算。

定义&#xff1a; 转移表就是一个函数指针数组。本质上是数组&#xff0c;数组的内容是函数指针&#xff0c;也就是函数地址&#xff0c;通过调用函数地址就可以调用函数的功能。 特别注明&#xff1a;假如函数名为Add&#xff0c;*Add的效果完全等于Add 为了弄清楚转移表的应…

​软考-高级-系统架构设计师教程(清华第2版)【第18章 安全架构设计理论与实践(P648~690)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第18章 安全架构设计理论与实践&#xff08;P648~690&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

视频剪辑技巧:简单步骤,批量剪辑并随机分割视频

随着社交媒体平台的广泛普及和视频制作需求的急剧增加&#xff0c;视频剪辑已经成为了当今社会一项不可或缺的技能。然而&#xff0c;对于许多初学者来说&#xff0c;视频剪辑可能是一项令人望而生畏的复杂任务。可能会面临各种困难&#xff0c;如如何选择合适的软件和硬件、如…

VBA技术资料MF84:判断文件夹是否存在并创建

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

【Qt-23】基于QCharts绘制曲线图

一、QChart简介 QChart是Qt中专门用于绘制图表的模块&#xff0c;支持折线图、柱状图、饼图等常见类型。其主要组成部分有&#xff1a; QChart&#xff1a;整个图表的容器&#xff0c;管理图表中的所有数据和图形属性QChartView&#xff1a;继承自QGraphicsView&#xff0c;用于…

单元测试实战(六)其它

为鼓励单元测试&#xff0c;特分门别类示例各种组件的测试代码并进行解说&#xff0c;供开发人员参考。 本文中的测试均基于JUnit5。 单元测试实战&#xff08;一&#xff09;Controller 的测试 单元测试实战&#xff08;二&#xff09;Service 的测试 单元测试实战&am…

基于单片机C51全自动洗衣机仿真设计

**单片机设计介绍&#xff0c; 基于单片机C51全自动洗衣机仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机C51的全自动洗衣机仿真设计是一个复杂的项目&#xff0c;它涉及到硬件和软件的设计和实现。以下是对这…

如何进行统一异常处理

我们在项目中可以使用ControllerAdvice注解结合ExceptionHandler用于全局异常的处理&#xff0c;ControllerAdvice学名是Controller增强器&#xff0c;作用是给Controller控制器添加统一的操作或处理。此注解结合方法型注解ExceptionHandler&#xff0c;可用于捕获Controller中…

【C++11】weak_ptr智能指针使用详解

系列文章目录 【C11】智能指针与动态内存 文章目录 系列文章目录简介一、头文件二、初始化及使用1. 使用一个shared_ptr来初始化 三、循环引用3.1 循环引用3.2 循环引用 解决方法 简介 在C编程中&#xff0c;处理循环引用是一个常见的问题。循环引用可能导致内存泄漏和资源管…

镀膜与干刻中的平均自由程是什么?

在芯片制造中&#xff0c;镀膜和干刻是其中的重要环节&#xff0c;通常要用到CVD&#xff0c;RIE等技术&#xff0c;对材料表面进行纳米级的精细操作。在这些工序中&#xff0c;原子&#xff0c;分子&#xff0c;离子等&#xff0c;会在气体或真空中进行自由运动&#xff0c;直…

IDEA 高分辨率卡顿优化

VM设置优化 -Dsun.java2d.uiScale.enabledfalse 增加该条设置&#xff0c;关闭高分切换 https://intellij-support.jetbrains.com/hc/en-us/articles/115001260010-Troubleshooting-IDE-scaling-DPI-issues-on-Windows​intellij-support.jetbrains.com/hc/en-us/articles/1…

金融业务系统: Service Mesh用于安全微服务集成

随着云计算的不断演进&#xff0c;微服务架构变得日益复杂。为了有效地管理这种复杂性&#xff0c;人们开始采用服务网格。在本文中&#xff0c;我们将解释什么是Service Mesh&#xff0c;为什么它对现代云架构至关重要&#xff0c;以及它是如何解决开发人员今天面临的一些最紧…

py 字符串转INT

在Python中&#xff0c;可以使用内置的int()函数将字符串转换为整数&#xff08;INT&#xff09;。以下是一个简单的示例&#xff1a; s "123" i int(s) print(i) # 输出&#xff1a;123在上述代码中&#xff0c;我们首先定义了一个字符串s&#xff0c;它包含了一…

centos三台主机配置互信ssh登录

1. 修改hosts信息 1.1三台主机上分别修改hosts文件 vi /etc/hosts1.2 三台主机分别填入如下内容&#xff0c;ip地址需要检查正确 192.168.126.223 node1 192.168.126.224 node2 192.168.126.225 node32. 秘钥生成和分发 2.1 在三台主机上分别生成秘钥 命令输入后&#xff…

剑指JUC原理-19.线程安全集合

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

IDEA-git commit log 线

一、本地代码颜色标识 红色&#xff1a;新建的文件&#xff0c;没有add到git本地仓库蓝色&#xff1a;修改的文件&#xff0c;没有提交到git远程仓库绿色&#xff1a;已添加到git本地仓库&#xff0c;没有提交到git远程仓库灰色&#xff1a;删除的文件&#xff0c;没有提交到g…

苍穹外卖--实现公共字段自动填充

也就是在插入或者更新的时候为指定字段赋予指定的值&#xff0c;使用它的好处就是可以统一对这些字段进行处理&#xff0c;避免了重复代码。在上述的问题分析中&#xff0c;我们提到有四个公共字段&#xff0c;需要在新增/更新中进行赋值操作。 实现步骤&#xff1a;* 1). 自定…

Redis 19 事务

Redis通过MULTI、EXEC、WATCH等命令来实现事务&#xff08;transaction&#xff09;功能。事务提供了一种将多个命令请求打包&#xff0c;然后一次性、按顺序地执行多个命令的机制&#xff0c;并且在事务执行期间&#xff0c;服务器不会中断事务而改去执行其他客户端的命令请求…

curl网络请求命令

curl简介 1、什么是curl2、curl命令的基本使用 1、什么是curl CURL&#xff08;CommandLine Uniform Resource Locator&#xff09;是一个利用URL语法&#xff0c;在命令行终端下使用的网络请求工具&#xff0c;支持HTTP、HTTPS、FTP等协议 Linux、MAC系统一般默认已安装好CUR…