智能指针

本文最后更新于:November 29, 2024 pm

万众瞩目的C++11终于引入了全新的基于RAII的三个智能指针类——std::unique_ptr,std:: shared_ptrstd::weak_ptr

此系列博客将会一一介绍三种类,使用注意事项,以及关于Pimpl的编程原则

裸指针的问题

使用过C的朋友对指针肯定不陌生,在C中,指针提供了对机器底层的访问接口,这也使得C和其它语言在内存访问上有比较大的不同。

如果在C++中继续使用C风格的裸指针进行动态资源的管理,就会暴露出很多很多问题:

  • 在声明一个裸指针时,并没有明确它应该指向一个单独的对象,还是一个数组,除非在变量名上显式写出

  • 在声明裸指针时,没有提示是否应该在使用完成之后就立刻将之释放掉

  • 即使我们知道了应该将之释放,我们也不知道应该如何操作,我们是应该使用`delete`操作符,还是应该调用一个专门的函数?

  • 即使知道了应该使用`delete`操作符,那么是`delete`还是`delete[]`?错误调用会导致未定义行为

  • 即使能确信,指针拥有其管理的对象,并且我们应该执行析构操作,但是要保证析构路上的所有代码都执行且只执行1次,仍然比较困难。未执行会导致资源回收失败,多此执行则会导致未定义行为

  • 没有什么正确的方式能够检测出指针是否是悬空的(dangle),也就是说无法断定其指向的内存是否已经被回收过了

虽然裸指针是一种强力的工具,近几十年的经验发现,只要在工程上稍微疏忽一下,裸指针就可以把人搞得晕头转向。

智能指针(Smart Pointer)是解决上述问题的一种方案之一。智能指针对裸指针进行了封装,它们的行为也被重载和包装得很像裸指针,但是规避了很多使用裸指针面临的风险。因此,学习了智能指针之后,在今后使用C++时,都应该优先选用智能指针,毕竟智能指针能做到几乎任何裸指针能做到的,但是犯错的几率会大大减少,但也只是减少。

C++11中一共有四种智能指针,std::auto_ptr, std::unique_ptr, std::shared_ptr, std::weak_ptr,它们都是为了管理动态资源而被设计出来的。具体地,它们保证动态对象在适当的时机以适当的方式析构(包括出现异常时),来防止资源泄露。

其中,std::auto_ptr是一个从C++98中残留下来的弃用特性,其自身在功能实现上,需要借助移动语义,但这是C++98并不具备的,所以通过拷贝的方式来做了变通,给人一种不伦不类的感觉。在C++11之后,其已经被std::unique_ptr取代,并且后者的能力更强。


为什么要强调动态资源/动态对象?因为博主在知乎等平台上看到一些数落C++智能指针缺陷的人,有些人会往智能指针里扔一个栈上的地址,等栈被弹出之后来一句,现在内存不安全了。有时这种回答的排名还很靠前,具有很强的误导性。

1
2
3
4
5
6
7
8
9
10
11
int* func(){
int tmep=123;
return &temp;
}
void process(){
auto ptr= std::make_shared<int>(func());
std::cout<<*ptr;// ub
/*
This is not how we utilize smart pointers!!!
*/
}

系列导航

Item18:使用std::unique_ptr来管理具备专属所有权的资源

Item19:使用std::shared_ptr来管理具备共享管理权的资源

Item20:对于类似std::shared_ptr但是可能空悬的指针,应当使用std::weak_ptr

Item21:优先使用std::make_unique和std::make_shared

Item22:使用Pimpl时,将特殊成员函数的定义放到实现文件中


内容出自对于 Effective Modern C++[1] Item18~22的学习笔记,并依照原文顺序组织

  1. Effective Modern C++ by Scott Meyers(O’Reilly). Copyright 2015 Scott Meyers, 978-1-491-90399-5

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!