C/C++编程:原始指针is bad

1059 篇文章 285 订阅

指向对象的原始指针不推荐使用,尤其当暴露给别的线程时。Observerable应当保存的不是原始的Observer *,而是别的什么东西,能分辨Observer 是否存活。类似的,如果Observer 要在析构函数里解注册,那么subject_的类型也不能是原始的Observerable*

有经验的C++程序员也许会想到智能指针。没错,这是正道,但也没那么简单,有些关窍需要注意。这两处直接使用shared_ptr是不行的,会形成循环引用,直接造成资源泄露
在这里插入图片描述

在这里插入图片描述

空悬指针

由两个指针p1和p2,指向堆上的同一个对象Object(下图左边),p1和p2位于不同的线程。假设线程A通过p1指针将对象销毁了,那么p2就成了空悬指针。这是一种典型的C/C++内存错误
在这里插入图片描述
要想安全的消耗对象,最好在别的线程都看不到的情况下,偷偷的做(这正是垃圾回收的原因,所有人都用不到的东西就是垃圾)

一个"解决方法"

一个解决空悬指针的方法是,引入一层间接性,让p1和p2所指的对象永久有效。比如下图中的proxy对象,这个对象,持有一个指向Object的指针(也就是说,p1和p2都是二级指针)
在这里插入图片描述
当销毁Object之后,proxy对象继续存在,其值变为0。而p2也没有变成空悬指针,它可以通过查看proxy的内容来判断Object是否还活着。如下图
在这里插入图片描述
要线程安全的释放Object也不是那么容易,race condition依旧存在。比如p2看第一眼是proxy不是0,正准确去调用Object的成员函数,期间对象已经被p1给销毁了。

问题在于,何时释放proxy指针呢?

一个更好的解决方法

为了安全的释放proxy,我们可以引入引用计数,再把p1和p2都从指针变成对象sp1和sp2。proxy现在有两个成员,指针和计数器

  • 一开始,由两个引用,计数值为2
    在这里插入图片描述
  • sp1析构了,引用计数的值减为1
    在这里插入图片描述
  • sp2也析构了,引用计数降为0,可以安全的销毁proxy和object了
    在这里插入图片描述
    等等,这不就是一个计数型智能指针吗?

一个万能的解决方案

引入另外一层间接性,用对象来管理共享资源(object),也就是handler/body的惯用技法。当然,编写线程安全、高效的handler难度很高,我们用现成的库就行

神器shared_ptr/weak_ptr

shared_ptr是引用计数型智能指针。shared_ptr< T>是一个类模板,它只有一个类型参数,使用起来很方便。引用计数是自动化资源管理的常用手法,当引用计数降为0时,对象就被消耗。weak_ptr也是一个引用计数型智能指针,但是它不增加对象的应用次数,即弱(weak)引用。

注意:

  • shared_ptr控制对象的生命周期。shared_ptr是强引用,只要有一个指向x对象的shared_ptr存在,该x对象就不会析构。当指向对象x的最后一个shared_ptr析构或者reset的时候,x保证会被销毁

系统的避免各种指针错误

C++里可能存续的内存问题大致有这么几个方面:

  • 缓冲区溢出
  • 空悬指针/野指针
  • 重复释放
  • 内存泄露
  • 不配对的new[]/delete
  • 内存碎片

正确的使用智能指针可以轻易解决前5个问题,解决第6个问题需要其他方法,比如内存池

  • 缓冲区溢出:用std::vector< char>/std::string或者自己编写Buffer class来管理缓冲区,自动记住缓冲区的长度,并通过成员函数而不是裸指针来修改缓冲区
  • 空悬指针/野指针:用shared_ptr和weak_ptr
  • 重复释放:用scoped_ptr,只在对象析构的时候释放一次
  • 内存泄露:用scoped_ptr,对象析构的时候自动释放内存
  • 不配对的new[]/delete:把new[]替换为std::vertor/scoped_array

在现代C++程序中一般不会出现delete语句,资源都是通过只能指针或者容器来管理的

另外,scoped_ptr/shared_ptr/weak_ptr都是值语义,要么是栈上对象,要么是其他对象的直接数据成员,或者标准库容器里面的元素。几乎不会出现下面这种用法

shared_ptr<Foo> *pFoo = new Shared_ptr<Foo>(new Foo); 

以及,如果这几种智能指针是对象x的数据成员,而它的模板参数T是个imcomplete类型,那么x的析构函数不能是默认的或者内联的,必须在.cpp文件里面显式定义

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值