C++使用智能指针的必要性

评判智能指针的标准

在C/C++中,指针很重要,却是麻烦的来源。如果一个指针建立是没有初始化,那就是野指针(Vaild Pointer);如果一个指针指向已经释放的内存,那就是空悬指针(Dangling Pointer)。

一些比 C/C++ 更高级的语言都要极力避开指针,比如 Java 使用了垃圾回收,让系统自动回收用户申请的资源。

C++ 的智能指针,始于 Boost 准标准库,在 C++11 正式加入到C++标准模板库(STL)中。

智能指针的核心思想是:“ 程序员负责申请资源,释放留给智能指针 ”。所以我们可以最直观的感受到智能指针的好处是:“ 申请资源一劳永逸,老板再也不用担心内存泄露 ”。

但如果我们对智能指针的理解仅限于此,我们就会受到很多反对的声音的干扰。

  • “ 当年的 auto_ptr 多么垃圾 ”
  • 智能会带来许多额外的开销,C/C++的效率高的优势荡然无存
  • 智能指针的风格和原来的 new 格格不入
  • 遇到某些复杂的数据结构,或者所有权不明确的场景,还是得裸指针来

但是,如果仅仅将智能理解为防止内存泄露,是十分片面的。在考虑是否使用智能指针的时候,我们应该看到,该项技术到底是弊大于利还是利大于弊?

多线程的情况

如果我们考虑到多线程的情况,那么智能指针的优势几乎是压倒性的。

如今有一个多线程的程序,多个线程共同持有一个对象。那么:
(1) 如何确保在对象析构的时候,没有其他线程正在使用对象资源呢?
(2) 如果确保在对象析构后,没有其他线程会后续使用对象资源呢?

对于第一个问题,我们可以在对象外部建立一套锁机制,在对象析构和引用对象的时候通过锁来确保排他性访问。
对于第二个问题,我们也可以在对象外部奖励一套信号量机制,每当有线程应用对象,便对信号量进行P操作来计数,使用完毕时进行V操作。

但是,如果对于每个共享资源,我们新建一套保护机制,那么对于程序员来说是非常大的负担,同时也容易出错。

智能指针恰恰帮程序员解决了这一痛点,因为智能指针有本身就是通过一套原子性的计数机制实现的。


下面是一些 shared_ptr 的笔记。

shared_ptr 的申请对象

使用 shared_ptr 申请对象最好的方法是通过 make_shared<T>(args)函数返回 shared_ptr。其中 args 是对象的构造函数的参数。

shared_ptr<T> sp = make_shared<T>( args );

// 相当于 T *p = new T( args );
shared_ptr 的拷贝构造
  • 如果已经申请对象,可以用拷贝构造使多个 shared_ptr 指向同一个对象。
  • 当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。
  • 当我们给shared_ptr赋予一个新值或是shared_ptr被销毁时,计数器就会递减。
  • 当最后一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象。
shared_ptr<T> p = make_shared<T>( args );
shared_ptr<T> q(p);  /* p和q指向同一个对象,计数器加1 */
shared_ptr 作为判断条件

shared_ptr 对象可以直接用于作为条件判断,如果p指向一个对象,则为true,否则为false。

shared_ptr<T> sp = make_shared<T>( args );

if( sp ) {
    cout << "true" << endl;
}
else {
    cout << "false" << endl;
}
shared_ptr 解引用

shared_ptr 在解引用上,和 c/c++ 指针是一致的。

shared_ptr<T> sp = make_shared<T>( args );

*sp;
sp -> f();
shared_ptr 独占判断

如果只有一个 shared_ptr 引用了对象,那么 unique() 返回 true,否则返回 false。

shared_ptr<T> sp = make_shared<T>( args );

if( sp.unique() ) {
    cout << "true" << endl;
}
else {
    cout << "false" << endl;
}
返回c/c++指针
  • 有些时候,只能使用c/c++指针。可以用成员函数get()来返回shared_ptr对应的c/c++指针。
  • 但是 get() 是不会增加计数器的,get()返回的指针,有可能在将来成为空悬指针。
  • 此外,函数 get() 返回的指针是不能 delete 的。
shared_ptr<T> sp = make_shared<T>( args );

T *p = sp.get();
附属品 weak_ptr

weak_ptr 是一种不控制所指向对象生存期的智能指针,它指向由一个 shared_ptr 管理的对象。将 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用计数。

一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有 weak_ptr 指向对象,对象还是会被释放,因此,weak_ptr 的名字抓住了这种智能指针“弱”共享对象的特点。

  • 当我们创建一个 weak_ptr 时,要用一个 shared_ptr 来初始化它:
shared_ptr<T> sp = make_shared<T>( args );

/* 使用shared_ptr来初始化weak_ptr而shared_ptr的计数值不变 */
weak_ptr<T> wp( sp );
  • 由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock()。
  • 此函数检查weak_ptr 指向的对象是否仍存在。如果存在,lock() 返回一个指向共享对象的shared_ptr。
  • 与任何其他shared_ptr类似,只要此shared_ptr存在,它所指向的底层对象也就会一直存在。
share_ptr<T> sp = make_shared<T>( args );
weak_ptr<T> wp( sp );

if( shared_ptr<T> np = wp.lock() ) {   // 如果np不为空则条件成立
    // 如果wp指向的对象存在,就将对象交给np,计数值加1
    // 因此在if中,该对象一直有效
}
  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值