当前位置: 首页 > 编程日记 > 正文

【C++】拷贝控制与资源管理

1. 拷贝控制与资源管理

管理类外资源的类必须定义拷贝控制成员。如P447中所见,这种类需要通过析构函数来释放对象所分配的资源。一旦一个类需要析构函数,那么几乎可确定它也需要一个拷贝构造函数和一个拷贝赋值函数。

明确拷贝语义:可以定义拷贝操作,使类的行为看起来像一个值或者一个指针。

类的行为像一个值,意味着他应该有自己的状态。深拷贝,副本与本身独立。

行为像指针的类则是一个共享状态。浅拷贝,副本与原对象使用相同的底层数据。改变副本也会改变原对象,反之亦然。

CP5_ex13_11_h 实例1:我们将在下一节中展示复制控制成员的定义。但是,您已经知道了实现这些成员所需要知道的一切。在继续阅读之前,先写HasPtr复制构造函数和复制赋值操作符。

假设我们希望HasPtr的行为像一个值。也就是说,每个对象都应该有自己的对象所指向的字符串副本。

#include <string>
​
class HasPtr {
public:HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }HasPtr& operator=(const HasPtr &hp) {auto new_p = new std::string(*hp.ps);delete ps;ps = new_p;i = hp.i;return *this;}~HasPtr() { delete ps;} 
private:std::string *ps;int i;
};

2. 行为像值的类:意味着他应该有自己的状态。深拷贝,副本与本身独立。

class HasPtr {
public:HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }//对ps指向的每一个string,每个HasPtr对象都有自己的拷贝HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }HasPtr& operator=(const HasPtr &hp) {auto new_p = new std::string(*hp.ps);//拷贝底层stringdelete ps;              //释放旧内存,当发生异常时,能将左侧对象置于以恶搞有意义的状态ps = new_p;             //更新指针指向新的分配的stringi = hp.i;               //从右侧数据拷贝数据到本对象 return *this;   //返回本对象}~HasPtr() { delete ps;} 
private:std::string *ps;int i;
};

赋值运算符中做了三件事情:
首先,就行了构造函数的工作:new_p的初始化器等价于HasPtr的拷贝构造函数中的ps的初始化器。

接着,与析构函数一样,我们delete当前ps指向的string。

然后,拷贝指向新分配的string指针,以及将i拷贝int值到本对象。

 HasPtr& operator=(const HasPtr &hp) {auto new_p = new std::string(*hp.ps);//拷贝底层stringdelete ps;              //释放旧内存,当发生异常时,能将左侧对象置于以恶搞有意义的状态ps = new_p;             //更新指针指向新的分配的stringi = hp.i;               //从右侧数据拷贝数据到本对象 return *this;   //返回本对象}

所以本质上:赋值运算符是组合了析构函数和拷贝构造函数的工作。这里的顺序很重要,必须先拷贝,再释放
一个好的模式是:先将右侧对象拷贝到一个临时对象中,当拷贝完成时,销毁左侧运算对象的现有成员就安全了。
一旦左侧对象先被释放或资源被销毁,就只剩下将数据从临时对象拷贝到右侧运算对象的成员中。

3. 行为像指针的类:是一个共享状态。浅拷贝,副本与原对象使用相同的底层数据。改变副本也会改变原对象。

对于行为类似指针的类,我们需要为其定义拷贝构造函数和拷贝赋值运算符,来拷贝指针成员本身而不是它指向的 string。我们的类仍然需要自己的析构函数来释放接受string参数的构造函数分配的内存(P447)。但是,在本例中,析构函数不能单万面地释放关联的string。只有当最后一个指向string的HasPtr销毁时,才可以释放string。

令一个类展现类似指针的行为的最好方法是使用shared ptr来管理类中的资源。但是,有时我们希望直接管理资源。在这种情况下,使用引用计数(reference count)(P402)就很有用了。为了说明引用计数如何工作,我们将重新定义HasPtr,令其行为像指针一样,但我们不使用shared_ptr,而是设计自己的引用计数。

引用计数的工作方式如下:

除了初始化对象外,每个构造函数(拷贝构造函数除外)还要创建一个引用计数,用来记录有多少对象与正在创建的对象共享状态。当我们创建一个对象时, 只有一个对象共享状态,因此将计数器初始化为

拷贝构造函数不分配新的计数器,而是拷贝给定对象的数据成员,包括计数器。拷贝构造函数递增共享的计数器,指出给定对象的状态又被一个新用户所共享。

析构函数递减计数器,指出共享状态的用户少了一个。如果计数器变为0,则析构函数释放状态。

拷贝赋值运算符递增右侧运算对象的计数器,递碱左侧运算对象的计数器。如果左侧运算对象的计数器变为0,意味着它的共享状态没有用户了,拷贝赋值运算符就必须销毁状态。

定义一个使用引用计数的类,类指针的拷贝成员,“篡改”引用计数

class HasPtr {
public://构造分配新的string和新的计数器,将计数器置为1HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0)use(new std::size_t(1)) { }//拷贝构造函数,拷贝所有的3个数据成员,并递增计数器HasPtr(const HasPtr &p) : ps(p.ps), i(p.i),use(p.use) {++ *use; }//HasPtr& operator=(const HasPtr &hp) {}~HasPtr() {} private:std::string *ps;int i;std::size_t *use;//用来记录有多少个对象共享 *ps 的成员
};

如上,当拷贝或赋值一个HasPtr对象时,希望副本和原对象都指向相同的string。即拷贝HasPtr对象时,我们拷贝ps本身,而不是ps指向的string。当我们拷贝时,还会递增string关联的计数器。

当拷贝构造函数给定HasPtr的所有3个函数成员时,构造函数还递增 use成员,指出ps和p.ps指向的string又有一个新的用户。

析构函数不能无条件的delete ps,可能还有其他对象指向这块内存。析构函数应该递减引用计数,指出共享string的对象少了一个。如果计数器变为0,则析构函数释放ps以及use指向的指针。

 HasPtr::~HasPtr() {if(-- *use == 0){delete ps;delete use;}
} 

拷贝赋值运算符一样执行拷贝构造函数与析构函数的工作。即,递增右侧运算对象的引用计数,递减左侧运算对象的引用计数,必要时释放内存。

HasPtr& HasPtr::operator= (const HasPtr &rhs)++*rhs.use;        //递增右侧对象的引用计数if(*--*use == 0){    //然后,递减本对象的引用计数delete ps;delete use;}ps = rhs.ps;        //将数据从rhs中拷贝到对象i =rhs.i;use=rhs.use;return *this        //返回本对象
}

Exercise 13.27 P475:定义自己的引用计数版本的HasPtr。

#include <string>class HasPtr {
public:HasPtr(const std::string &s = std::string()) :ps(new std::string(s)), i(0), use(new size_t(1)) { }HasPtr(const HasPtr &hp) : ps(hp.ps), i(hp.i), use(hp.use) { ++*use; }HasPtr& operator=(const HasPtr &rhs) {++*rhs.use;if (--*use == 0) {delete ps;delete use;}ps = rhs.ps;i = rhs.i;use = rhs.use;return *this;}~HasPtr() {if (--*use == 0) {delete ps;delete use;}} 
private:std::string *ps;int i;size_t *use;
};

Exercise 13.28:给定以下类,实现默认构造函数和必要的复制控制成员。

#include <string>
using std::string;class TreeNode {
public:TreeNode() : value(string()), count(new int(1)), left(nullptr), right(nullptr) { }TreeNode(const TreeNode &rhs) : value(rhs.value), count(rhs.count), left(rhs.left), right(rhs.right) { ++*count; }TreeNode& operator=(const TreeNode &rhs);~TreeNode() {if (--*count == 0) {delete left;delete right;delete count;}}private:std::string value;int         *count;TreeNode    *left;TreeNode    *right;
};class BinStrTree {
public:BinStrTree() : root(new TreeNode()) { }BinStrTree(const BinStrTree &bst) : root(new TreeNode(*bst.root)) { }BinStrTree& operator=(const BinStrTree &bst);~BinStrTree() { delete root; }private:TreeNode *root;
};

相关文章:

leangoo V5.4.2版上线

本次更新增加了“卡片编辑面板内显示成员、截止日期、工作量”的功能。同时我们也进行了大量的功能优化&#xff0c;以下是此次更新详情&#xff1a; 1. 新增“卡片编辑面板内显示成员、截止日期、工作量”功能 本次更新后 &#xff0c;您在卡片编辑面板内添加成员&#xff0c;…

差分边缘检测实现

#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/opencv.hpp> using namespace cv; // 图像差分操作 void diffOperation(const cv::Mat srcImage, cv::Mat& edgeXImage,cv::Mat& edgeYImage) {cv::Mat…

2.1:CGPROGRAM

文章著作权归作者所有。转载请联系作者&#xff0c;并在文中注明出处&#xff0c;给出原文链接。 本系列原更新于作者的github博客&#xff0c;这里给出链接。 前言 经过前面两个章节的铺垫&#xff0c;我们对渲染以及Unity Shaderlab相关的知识已经有了大概的认识&#xff0c;…

【OpenCV】OpenCV中积分图函数与应用

OpenCV中积分图函数与应用 参考资料 opencv 查找integral&#xff0c;目前网上大部分的资料来自于opencv https://docs.opencv.org/master/d7/d1b/group__imgproc__misc.html#gadeaf38d7701d7ad371278d663c50c77dhttps://blog.csdn.net/jia20003/article/details/52710751ht…

django学习笔记【003】创建第一个带有model的app

【1】python应用程序要连接mysql有多个驱动程序可供选择&#xff1a; 1、MySQLdb 这个只支持python2.x 所以在这里就不说了&#xff1b; 2、mysqlclient 下载地址   https://pypi.python.org/pypi/mysqlclient/1.3.9 3、MySQL Connector/python 这个是mysql官方主推的mysql驱…

图像非极大值抑制 Sobel 边缘实现

bool SobelVerEdge(cv::Mat srcImage, cv::Mat& resultImage) {CV_Assert(srcImage.channels() 1);srcImage.convertTo(srcImage, CV_32FC1);// 水平方向的 Sobel 算子cv::Mat sobelx (cv::Mat_<float>(3,3) << -0.125, 0, 0.125,-0.25, 0, 0.25,-0.125, 0, …

第四次作业 (日期和jieba库的运用)

设计题1&#xff1a; 设计一个本月份日历&#xff0c;输出格式如下&#xff1a; 要求&#xff1a; 1.初始化start_day&#xff0c;end_day两个日期 from datetime import datetime start_daydatetime(2019,4,1) end_daydatetime(2019,4,30) 其它时间数据生成要用datetime或date…

【C++】LINK类型错误分析记录

LINK类型错误 情况1&#xff1a; 根据生成路径&#xff0c;查找是否成功生成静态库/动态库&#xff0c;一般在./bin文件中。 情况2&#xff1a; 是否在CMakeLists中target_link_libraries中添加链接静态库操作 情况3&#xff1a; 是都存在类模板&#xff0c;需要实例化&a…

eBay宣布发布全新的购买和销售APIs

eBay最近宣布发布两款全新的购买和销售APIs。这些APIs旨在促进eBay产品在第三方应用程序中的更好集成。eBay于10月19日在他们的博客上发表了几篇文章&#xff0c;不仅详细介绍了这些全新的购买和销售APIs提供的功能&#xff0c;而且还详细地总结了他们公司从SOAP&#xff08;简…

Sobel 边缘实现

#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgproc/imgproc.hpp" #include <iostream> using namespace cv; // 非极大值抑制实现sobel竖直细化边缘 bool SobelVerEdge(cv::Mat srcImage, cv::…

vue下实现textarea类似密码框的功能之探索input输入框keyup,keydown,input事件的触发顺序...

项目中引入element的input框组件&#xff0c;触发事件必须要加上.native <el-input placeholder"请输入" type"textarea" v-model"valueText" keyup.native"keyUp(valueText,$event)" keydown.native"keyDown($event)" …

【C++】动态内存管理/move/以及移动构造与移动赋值运算符

文章目录1 .对象移动与右值引用 实际应用过程中遇到的问题及其解决方案c中临时变量不能作为非const的引用参数2. 动态内存管理类3. 对象移动与右值引用4. 移动构造与移动复制运算符1 .对象移动与右值引用 实际应用过程中遇到的问题及其解决方案 问题描述&#xff1a; bool Cr…

图像直接卷积 Sobel 边缘实现

bool sobelEdge(const cv::Mat& srcImage, cv::Mat& resultImage,uchar threshold) {CV_Assert(srcImage.channels() 1);// 初始化水平核因子Mat sobelx (Mat_<double>(3, 3) << 1, 0,-1, 2, 0, -2, 1, 0, -1);// 初始化垂直核因子Mat sobely (Mat_&…

JSON.parse解析特殊字符报错解决方案

2019独角兽企业重金招聘Python工程师标准>>> 具体案例&#xff1a; 页面点击”下一任务” 会去请求后台&#xff0c;这里出现的问题是有虚拟任务的时候。然后会返回一个map&#xff0c;也就是如下图中回调函数中的data。 当该map里存有以下字符的时候&#xff1a; \…

MySQL数据库字符集和整理

MySQL数据库字符集和整理(2009-11-20 22:23:37)mysql数据库 it 其实这个表在MySQL数据库中通过phpMyAdmin就能看到&#xff0c;icech只是把表格整理了一下方便大家使用&#xff0c;如果要更换数据库的字符集&#xff0c;心里有数。其中有三种utf8_general_ci、utf8_unicode_ci…

【SLAM后端】—— ceres优化相机位姿求解

求解结果如下&#xff1a; mat 初始化&#xff0c;eigenvalue初始化 Mat K ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );Eigen::Matrix<float,3,1> vd_3d;v_3d << 3, 2, 1;求解目标函数结构体构造与实例 struct CurveFi…

SPOJ 1811 LCS [后缀自动机]

题意&#xff1a; 求两个串的最大连续子串 一个串建SAM&#xff0c;另一个串在上面跑 注意如果走了Suffix Link&#xff0c;sum需要更新为t[u].val1 Suffix Link有点像失配吧&#xff0c;当前状态s走不了了就到Suffix Link指向的状态fa上去&#xff0c;fa是s的后缀所以是可行的…

图像卷积下非极大值抑制 Sobel 的实现

bool sobelOptaEdge(const cv::Mat& srcImage, cv::Mat& resultImage, int flag) {CV_Assert(srcImage.channels() 1);// 初始化sobel水平核因子cv::Mat sobelX (cv::Mat_<double>(3, 3) << 1, 0, -1,2, 0, -2, 1, 0, -1);// 初始化sebel垂直核因子cv::…

was unable to refresh its cache! status = Cannot execute request on any known server

出现这种错误是因为: Eureka服务注册中心也会将自己作为客户端来尝试注册它自己&#xff0c;所以我们需要禁用它的客户端注册行为。 在 yml中设置 eureka.client.register-with-eurekafalse eureka.client.fetch-registryfalse 但在服务端是要这是为false的&#xff0c;在客…

【C++】浅析析构函数(基类中)为什么要写成虚基类?

为什么有了虚析构函数&#xff0c;就能先调用子类的析构函数&#xff1f; class A {virtual ~A(){} };class B : A {virtual ~B(){} };A *p new B(); delete p; 唯一差别是&#xff0c;每个析构函数结束时会自动&#xff08;隐含地&#xff09;调上父类的析构函数&#xff0…

Roberts 边缘检测

#include <opencv2/opencv.hpp> // roberts算子实现 cv::Mat roberts(cv::Mat srcImage) {cv::Mat dstImage srcImage.clone();int nRows dstImage.rows;int nCols dstImage.cols;for (int i 0; i < nRows - 1; i){for (int j 0; j < nCols - 1; j){// 根据公…

vector、map删除当前记录

map<string, string> sMap; map<string, string>::iterator iter; for(iter sMap.begin();iter ! sMap.end();/* iter */) {sMap.erase(iter); }注意下列错误表达&#xff1a;1. for(iter sMap.begin();iter ! sMap.end(); iter ) {sMap.erase(iter); } 错误原因…

1-2 postman工具简介

postman提供了一个多窗口和多选项卡页面用于发送和接受请求&#xff0c;postman努力保持整洁和灵活&#xff0c;提供更多的空间&#xff0c;满足用户的需要。他很简单&#xff0c;能满足大部分接口的测试&#xff0c;性价比特别高。如图所示&#xff1a; 1.侧边栏 postman的侧边…

【C++】重载运算符(一)

1.1 重载运算符特点 重载运算符本质上是一次函数调用 除了operator() 运算符调用外&#xff0c;其他重载运算符不能含有默认参数。 当重载的运算符是成员函数时&#xff0c;this绑定到左侧运算对象。成员运算符函数&#xff08;显式&#xff09;的参数数量比运算对象少一个。…

javaScript的调试(二)

2019独角兽企业重金招聘Python工程师标准>>> 一、Firebug Firebug是Firefox浏览器的调试工具&#xff0c;只要我们在Firefox中安装了Firebug应用&#xff0c;就可以按F12或右击鼠标开启调试 那么我们就先来看一下如何在Firefox中安装了Firebug应用&#xff0c;一图剩…

Prewitt 边缘检测

#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgproc/imgproc.hpp" #include <iostream> // prewitt算子实现 cv::Mat prewitts(cv::Mat img, bool verFlag false) {img.convertTo(img, CV_32FC…

[swift 进阶]读书笔记-第十一章:互用性 C11P1 实践:封装 CommonMark

第十一章&#xff1a;互用性 Interoperability 前言&#xff1a; swift 的最大优点就是与C 或者 OC 混编的时候稳的一匹 本章主要讲了swift和C之间的一些知识点。 11.1 实践:封装 CommonMark Hands-On: Wrapping CommonMark 这一小节更像是一个教程。教你如何封装C语言中的Comm…

【Cmake】Cmake学习记录

Cmake学习记录 1.1 常例 add_library(gen_reference_infogen_reference_info/gen_reference_info.hgen_reference_info/gen_reference_info.cc ) target_link_libraries(gen_reference_infoparamsimage_preprocessorcenter_extractorremapperdescriptor_generator${OpenCV_LI…

MySQL(三)用正则表达式搜索

正则表达式是用来匹配文本的特殊的串&#xff08;字符集合&#xff09;&#xff0c;将一个模式&#xff08;正则表达式&#xff09;与一个文本串进行比较&#xff1b; 所有种类的程序设计语言、文本编辑器、操作系统等都支持正则表达式&#xff0c;正则表达式用正则表达式语言来…

计算梯度幅值与方向

Mat magX Mat(src.rows, src.cols, CV_32F); Mat magY Mat(src.rows, src.cols, CV_32F); Sobel(image, magX, CV_32F, 1, 0, 3); Sobel(image, magY, CV_32F, 0, 1, 3); // 计算斜率 Mat slopes Mat(image.rows, image.cols, CV_32F); divide(magY, magX, slopes); // 计…