13-2 c++拷贝控制和资源管理
2022/3/1 17:22:56
本文主要是介绍13-2 c++拷贝控制和资源管理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录- 13.2.1 行为像值的类
- 类拷贝赋值运算符的编写
- 13.2.2 定义行为像指针的类
- 引用计数
- 定义一个使用引用计数的类
为了定义这些成员,我们首先必须确定此类型对象的拷贝语义。一般来说,有两种选择:可以定义拷贝操作,使类的行为看起来像一个值或者像一个指针。
- 类的行为像一个值,意味着它应该也有自己的状态。当我们拷贝一个像值的对象时,副本和原对象是完全独立的。改变副本不会对原对象有任何影响,反之亦然。
- 行为像指针的类则共享状态。当我们拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。改变副本也会改变原对象,反之亦然。
13.2.1 行为像值的类
定义一个HasPtr类,该类需要
- 定义一个拷贝构造函数,完成string的拷贝,而不是拷贝指针
- 定义一个析构函数来释放string
- 定义一个拷贝赋值运算符来释放对象当前的 string,并从右侧运算对象拷贝string
HasPtr的类值版本如下:
class HasPtr{ public: HasPtr(const &string &s = string()) : ps(new string(s)), i(0){} //对于ps指向的string,每个HasPtr都有自己的拷贝 HasPtr(const HasPtr &p): ps(new string(*p.ps)), i(p.i){} HasPtr& operator=(const HasPtr &p); ~HasPtr() {delete ps;} //记得释放内存 private: string *ps; int i; };
类拷贝赋值运算符的编写
赋值运算符组合了析构函数和构造函数的操作
这些操作需要以正确的顺序执行
- 先拷贝右侧对象
- 处理自赋值情况
- 异常安全
- 释放左侧对象的资源
- 更新左侧对象
HasPtr& HasPtr::operator=(const HasPtr &rhs){ string *newp = new string(*rhs.ps);//拷贝底层string delete ps; //释放旧内存 ps = newp; //更新左对象 i = rhs.i; return *this; //返回本对象 }
在定义赋值运算符时要注意两点:
- 能处理自赋值。
- 大多数赋值运算符组合了析构函数和拷贝构造函数的工作。
如果操作顺序错误,先销毁左侧对象再拷贝,就无法处理自赋值
13.2.2 定义行为像指针的类
需要定义拷贝构造函数和拷贝运算符,拷贝指针而不是string
析构函数要释放string,在本例中,只有当最后一个指向string的ps被销毁后才能释放底层string的内存,需要用到shared_ptr
使用shared_ptr的关键作用之一是引用计数,记录指向对象的指针有多少个,在此我们不直接使用shared_ptr,而是自主实现引用计数,加强理解
引用计数
工作方式
- 除了初始化对象外,每个构造函数(拷贝构造函数除外)还要创建一个引用计数,用来记录有多少对象与正在创建的对象共享状态。当我们创建一个对象时,只有一个对象共享状态,因此将计数器初始化为1。
- 拷贝构造函数不分配新的计数器,而是拷贝给定对象的数据成员,包括计数器。拷贝构造函数递增共享的计数器,指出给定对象的状态又被一个新用户所共享。
- 析构函数递减计数器,指出共享状态的用户少了一个。如果计数器变为0,则析构函数释放状态。
- 拷贝赋值运算符递增右侧运算对象的计数器,递减左侧运算对象的计数器。如果左侧运算对象的计数器变为0,意味着它的共享状态没有用户了,拷贝赋值运算符就必须销毁状态。
在哪里存放计数器?
不能作为类的成员
HasPtr p1("Hi"); HasPtr p2(p1); HasPtr p3(p1);
创建p3时,我们可以递增p1的计数器并把它拷贝到p3中,但是,p2的计数器没有变化
所以:要让所有对象共享计数器的底层数据,需要把计数器定义在动态内存中
定义一个使用引用计数的类
指针版本的HasPtr
class HasPtr{ public : //构造函数分配新的构造器,初始化为1 HasPtr(const string &s = string()) : ps(new string(s)), i(0), use(new int(1)){} //拷贝构造函数拷贝所有成员,计数器+1 HasPtr(const HasPtr &p) : ps(p.ps), i(p.i), use(p.use){++*use;} HasPtr& operator=(const HasPtr&); ~HasPtr(); private : string *ps; int i; int *use; //记录有多少个共享*ps的对象 }; //析构函数 HasPtr::~HasPtr(){ //递减计数器,计数==0时释放内存 if(--*use == 0){ delete ps; //释放string内存 delete use; //释放计数器内存 } } //拷贝赋值 HasPtr& 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; //返回本对象 }
这篇关于13-2 c++拷贝控制和资源管理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享