Effective C++——》条款13:以对象管理资源

从这个条款开始,就进入到资源管理部分了。资源管理往往是大型项目的一个难点,也是重中之重,看到一些编程规范,都是将资源管理的规范列为高优先级的重点。

管理资源的最好方法其实是预防,而好的预防方法就是尽量不去使用C/C++的原生指针,这些指针像幽灵一样,一个“忘记”,就是一个隐患。当项目小的时候,这些隐患看不出来,但当研发一个拥有上万级用户的产品时,服务器对很多人同时运行含有隐患的代码,这个隐患就会爆发,导致内存不足而崩溃

举个例子,初学者常常这样写:

1 int *p = new int();
2 3 delete p;

这个当然是OK的,内存可以回收,但万一中间的过程超过数十行,你还能记住去delete p吗?

有些同学可能会这样做,每一个new之后,先写好delete,再往中间插代码,但如果遇到这样的情况:

1 int* GetResource()
2 {
3  return new int(10);
4 }

这个函数负责生成资源,并将之返回给上一层,因为这个资源是要在上一层用的,所以在函数内还不能直接delete,这可就麻烦了。程序员们可要随时记挂着这个内存,在不再需要的时候赶紧释放了。但一切可能出错的地方真的会出错,代码一多,记性就不那么好了,很容易出现忘记delete的事情。

 

那么有没有什么好的方法,可以让我们痛快地管理资源呢?这就牵涉到智能指针的问题了。常用的智能指针有memory头文件里面的auto_ptr,还有boost库里的shared_ptr。两者的实现机制不同,但功能是类似的,就是能自动管理资源。他们体现的思想就是RAIIResources Acquisition Is Initialzeation 资源取得的时机便是初始化时机)。

 

举个形象的例子就是:

auto_ptr<int> ap(new int(10));

或者是:

shared_ptr<int> sp(new int(10));

可以看到,这里根本就没有出现原生指针,而是直接将new出来的内存交给了一个“管理者”。这个管理者可以是auto_ptr,也可以是shared_ptr,他们的存在,使得程序员可以真的忘记delete了,当ap或者sp的生命周期结束时,他们会将资源释放出来,交还给系统。比如:

复制代码
1 void fun()
2 {
3 auto_ptr<int> ap(new int(10);
4 *ap = 20;
5 }
6 // ap 在fun()结束后会自动回收掉
复制代码

auto_ptr和shared_ptr的区别在于管理方法不同。

 

auto_ptr这个管理者是一个很霸气的boss,他只想独管资源,而不允许其他管理者插手,否则他就退出管理,把这个资源交给另一方,自己再也不碰了

举个例子:

auto_ptr<int> boss1(new int(10)); // 这个时候boss1对这个资源进行管理

auto_ptr<int> boss2(boss1); // 这个时候boss2要插手进来,boss1很生气,所以把管理权扔给了boss2,自己就不管事了。所以boss2持有这个资源,而boss1不再持有(持有的是null)。

所以同一时刻,只有一个auto_ptr能管理相同的资源

这里还是有必要解释一下这句话的,比如:

1 auto_ptr<int> boss1(new int(10)); 
2 auto_ptr<int> boss2(new int(10));

boss1还持有资源吗?答案是有的,因为new执行了两次,虽然内存空间里的初始值是一样的,但地址并不同,所以不算相同的资源。

再来,为了把问题讲清楚,这里就让原生指针再出场一下:

1 int *p = new int(10);
2 auto_ptr<int> boss1(p);
3 auto_ptr<int> boss2(p);

boss1还持有资源吗?

 

答案是当程序运行到第三句的时候,就弹出assertion failed的红叉,程序崩溃了,为什么会这样呢?因为p所指向的是同一块资源,所以boss2发出管理手段后,boss1肯定要有动作的。第三句话调用boss2的构造函数,但构造函数中boss2识别出来p所指向的资源已经被占用了,所以会assertion failed。

事实上,这种原生指针和智能指针混用的程序是非常不好的,如果不使用原生指针,上面的崩溃问题就不会发生。

 

因为auto_ptr常常发生管理权的交付,所以用作形参的时候一定要小心

若有一个函数是:void fun(auto_ptr<int> ap){}

在主函数中调用fun(main_ap);

main_ap就会失去管理权了

正是因为这个特性,所以auto_ptr不支持STL容器,因为容器要求“可复制”,但auto_ptr只能有一个掌握管理权。另外,auto_ptr也不能用于数组,因为内部实现的时候,用的是delete,而不是delete[]。

 

shared_ptr则顾名思义,他是一个分享与合作的管理者,他的内部实现是引用计数型的,每多一次引用,计数值就会+1,而每一次引用的生命周期结束时,计数值就会-1,当计数值为0的时候,说明内存没有管理者管理了,最后一个管理这个内存的管理者就会将之释放。

再回到我们之前的例子:

1 shared_ptr<int> boss1(new int(10)); // 这个时候boss1对这个资源进行管理
2 shared_ptr<int> boss2(boss1); // boss2携手boss1对这个资源进行管理

注意这个时候boss1并没有交出管理权,boss2的加入只对对象内部的计数指针造成了影响,在外部就像什么也没发生一样,可以使用boss1和boss2来管理资源。


好,最后总结一下:

1. 为了防止资源泄漏,使用RAII思想,它们在构造函数中获得资源,并在析构函数中释放资源

2. 两个常用的RAII类是shared_ptrauto_ptr,但前者一般是更佳的选择,因为其copy行为比较直观,若选择auto_ptr,复制动作会使它指向null。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
疫情居家办公系统管理系统按照操作主体分为管理员和用户。管理员的功能包括办公设备管理、部门信息管理、字典管理、公告信息管理、请假信息管理、签到信息管理、留言管理、外出报备管理、薪资管理、用户管理、公司资料管理管理管理。用户的功能等。该系统采用了MySQL数据库,Java语言,Spring Boot框架等技术进行编程实现。 疫情居家办公系统管理系统可以提高疫情居家办公系统信息管理问题的解决效率,优化疫情居家办公系统信息处理流程,保证疫情居家办公系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理疫情居家办公系统信息,包括外出报备管理,培训管理,签到管理,薪资管理等,可以管理公告。 外出报备管理界面,管理员在外出报备管理界面中可以对界面中显示,可以对外出报备信息的外出报备状态进行查看,可以添加新的外出报备信息等。签到管理界面,管理员在签到管理界面中查看签到种类信息,签到描述信息,新增签到信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值