尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C++中的Primer拷贝控制和资源管理详解
贝控制和资源管理通常管理类外资源的类必须定义拷贝控制成员这种类需要通过析构函数来释放对象所分配的资源。一旦一个类需要析构函数那么它儿乎肯定也需要一个拷贝构造函数和一个拷贝赋值运算符。为了定义这些成员我们首先必须确定此类型对象的拷贝语义。一般来说有两种选择可以定义拷贝操作使类的行为看起来像一个值或者像一个指针。类的行为像一个值意味着它应该也有自己的状态。当我们拷贝一个类值的对象时副本和原对象是完全独立的。改变副本不会对原对象有任何影响反之亦然。行为像指针的类则共享状态。当我们拷贝一个这种类的对象时副本和原对象使用相同的底层数据。改变副本也会改变原对象反之亦然。在我们使用过的标准库类中标准库容器和string类的行为像一个值。而不出意外的shared_ptr类提供类似指针的行为就像我们的StrBlob类一样IO类型和unique_ptr不允许拷贝或赋值因此它们的行为既不像值也不像指针。为了说明这两种方式我们会为练习中的HasPtr类定义拷贝控制成员。首先我们将令类的行为像一个值;然后重新实现类使它的行为像一个指针。我们的HasPtr类有两个成员一个int和一个string指针。通常类直接拷贝内置类型(不包括指针)成员;这些成员本身就是值因此通常应该让它们的行为像值一样。我们如何拷贝指针成员决定了像HasPtr这样的类是具有类值行为还是类指针行为。行为像值的类为了提供类值的行为对于类管理的资源每个对象都应该拥有一份自己的拷贝。这意味着对于ps指向的string每个HasPtr对象都必须有自己的拷贝。为了实现类值行为HasPtr需要定义一个拷贝构造函数完成string的拷贝而不是拷贝指针定义一个析构函数来释放string定义一个拷贝赋值运算符来释放对象当前的string并从右侧运算对象拷贝string类值版本的HasPtr如下所示1234567891011121314classHasPtr{public:HasPtr(conststd::string sstd::string()):ps(newstd::string(s)1),i(0){}//对ps指向的string,每个BasPtr对象都有自己的拷贝HasPt(constHasPtrp):ps(newstd::string(*p.ps)),i(p.i){}HasPtr operator(constHasPtr);~HasPtr(){deleteps;}private:std::string*ps;inti;}我们的类足够简单在类内就已定义了除赋值运算符之外的所有成员函数。第一个构造函数接受一个(可选的)string参数。这个构造函数动态分配它自己的string副本并将指向string的指针保存在ps中。拷贝构造函数也分配它自己的string副本。析构函数对指针成员ps执行delete释放构造函数中分配的内存。类值拷贝赋值运算符赋值运算符通常组合了析构函数和构造函数的操作。类似析构函数赋值操作会销毁左侧运算对象的资源。类似拷贝构造函数赋值操作会从右侧运算对象拷贝数据。但是非常重要的一点是这些操作是以正确的顺序执行的即使将一个对象赋予它自身也保证正确。而且如果可能我们编写的赋值运算符还应该是异常安全的一一当异常发生时能将左侧运算对象置于一个有意义的状态。在本例中通过先拷贝右侧运算对象我们可以处理自赋值情况并能保证在异常发生时代码也是安全的。在完成拷贝后我们释放左侧运算对象的资源并更新指针指向新分配的string:12345678HasPtrHasPtr::operator(constHasPtrrhs){auto newpnewstring(*rhs.ps);//指贝底层stringdeleteps;//释放旧内存ps newp;//从右侧运算对象指贝数据到本对象i rhs;return*this;//返回本对象}在这个赋值运算符中非常清楚我们首先进行了构造函数的工作:newp的初始化器等价于HasPtr的拷贝构造函数中ps的初始化器。接下来与析构函数一样我们delete当前ps指向的string。然后就只剩下拷贝指向新分配的string的指针以及从rhs拷贝int值到本对象了。关键概念:赋值运算征当你编写赋值运算符时:有两点需要记住:如果将一个对象赋予它自身贼值运算待必须能正确工作。天多数赋值运算符组各了析构函数和拷贝构造函数的工作。当你编写一个赋值运算符时一个好的模式是先将右侧运算对象赋值到一个局部临时对象中。当拷贝完成后销毁左侧运算对象的现有成员就是安全的了。一旦左侧运算对象的资源被销毁就只剩下将数据从临时对象拷贝到左侧运算对象的成员中了。为了说明防范自赋值操作的重要性考虑如果赋值运算符如下编写将会发生什么12345678910//这样编写赋值运算符是错误的!HasPtrHasPtr::operator(constHasPtrzhs){deleteps;//释放对象指向的string//如果rhs和*this是同一个对象,我们就将从已释放的内存中拷贝数据!psnewstring(*(rhs.ps));irhs.i;return*this;}如果zhs和本对象是同一个对象delete ps会释放this和rhs指向的string。接下来当我们在new表达式中试图拷贝(rhs.ps)时就会访问一个指向无效内存的指针其行为和结果是未定义的。这样我们的StrBlobPtr类就仍能使用指向vector的weak_ptr了。你修改后的类将需要一个拷贝构造函数和一个拷贝赋值运算符但不需要析构函数。解释拷贝构造出数和拷贝赋值运算符必须要做什么。解释为什么不需要析构函数。定义行为像指针的类对于行为类似指针的类我们需要为其定义拷贝构造函数和拷贝赋值运算符来拷贝指针成员本身而不是它指向的string。我们的类仍然需要自己的析构函数来释放接受string参数的构造函数分配的内存。但是在本例中析构函数不能单方面地释放关联的string。只有当最后一个指向string的HasPtr销毁时它才可以释放string。令一个类展现类似指针的行为的最好方法是使用shared_ptr来管理类中的资源。拷贝(或赋值)一个shared_ptr会拷贝(赋值)shared_ptr所指向的指针。shared_ptr类自己记录有多少用户共享它所指向的对象。当没有用户使用对象时shared_ptr类负责释放资源。但是有时我们希望直接管理资源。在这种情况下使用引用计数(reference count)就很有用了。为了说明引用计数如何工作我们将重新定义HasPtr令其行为像指针一样但我们不使用shared_ptr而是设计自己的引用计数。引用计数引用计数的工作方式如下除了初始化对象外每个构造函数(拷贝构造函数除外)还要创建一个引用计数用来记录有多少对象与正在创建的对象共享状态。当我们创建一个对象时只有一个对象共享状态因此将计数器初始化为1。拷贝构造函数不分配新的计数器而是拷贝给定对象的数据成员包括计数器。拷贝构造函数递增共享的计数器指出给定对象的状态又被一个新用户所共享。“析构函数递减计数器指出共享状态的用户少了一个。如果计数器变为0则析构函数释放状态。拷贝赋值运算符递增右侧运算对象的计数器递减左侧运算对象的计数器。如果左侧运算对象的计数器变为0意味着它的共享状态没有用户了拷贝赋值运算符就必须销毁状态。
RELATED

相关推荐

c++中移动语义和完美转发及易错点

c++中移动语义和完美转发及易错点

C 中的移动语义和完美转发是 C11 引入的两个重要特性,它们分别用于提高性能和灵活性。移动语义(Move Semantics):移动语义允许有效地将资源(如堆上分配的内存或其他资源)从一个对象转移到另一个对象,而不是…

📅 2026/10/4 14:48:09
android.database.StaleDataException 排查:Cursor 关闭后仍被访问,TaoToken 统一 Key 通道下的复现与修复

android.database.StaleDataException 排查:Cursor 关闭后仍被访问,TaoToken 统一 Key 通道下的复现与修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/4 14:48:09
碳机制下的综合能源优化:需求响应、共享储能与碳捕集协同建模

碳机制下的综合能源优化:需求响应、共享储能与碳捕集协同建模

碳机制不是给综合能源系统加一个惩罚项,而是让整套调度逻辑从"跟着电价走"变成"跟着电价和碳价一起走"。这篇文章我从实际做过的一个园区综合能源优化模型出发,把需求响应、柔性负荷、蓄冷式空调、共享储能、碳捕集这几个资源怎么建…

📅 2026/10/4 14:43:09
MORE NEWS

更多资讯

📰

stack/queue中的deque

目录 摘要: 一:stack和queue叫适配器的原因 二:stack和queue的写法固定的原因 1:stack实现代码 2:queue实现代码 3:写法固定的原因 三:deque的优缺点 1:deque的优点 2&…

📰

Trae安装后连不上模型?把Base URL改到TaoToken的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Python环境变量配置全攻略:从PATH原理到虚拟环境实战

1. 先说清楚:环境变量到底是个啥说实话,我当年第一次接触环境变量,完全是被安装 JDK 的材料逼着去配的。那时候看不懂 PATH 里一串串用分号隔开的路径,只知道照着教程抄,抄错了就重来,烦得要命。后来自己给…

📰

C语言:系统开发的基石与现代编程的起点

C 简介C语言, 属于一种通用的高级语言, 它最初是由丹尼斯里奇在贝尔实验室设计的, 目的是用于开发UNIX操作系统。C语言在1972年, 首次在DEC PDP - 11计算机上被给予了实现。1978年时, 有这么两个人, 一个叫布莱恩柯林汉(Brian), 另一个叫丹尼斯里奇&…

📰

Learn X in Y Minutes 贡献指南:从文章规范、Frontmatter 配置到本地站点构建全流程

文档教程 【免费下载链接】learnxinyminutes-docs Code documentation written as code! How novel and totally my idea! 项目地址: https://gitcode.com/gh_mirrors/le/learnxinyminutes-docs 点击查看 免费下载 Learn X in Y Minutes(learnxinyminut…

📰

基于Java的人事管理系统:从环境配置到二次开发全攻略

简介:这是一份基于Java Web技术的人事人力资源管理系统完整项目包,面向正在做毕业设计、课程设计或期末大作业的计算机专业学生,也可作为JSPMySQL入门项目的参考范例。压缩包共116个文件,包含69个JSP页面、6个JS脚本、4个CSS样式及…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬