往期精彩:
- Modern C++ 学习笔记——易用性改进篇
- Modern C++ 学习笔记 —— 右值、移动篇
- Modern C++ 学习笔记 —— 智能指针篇
- Modern C++ 学习笔记 —— lambda表达式篇
- Modern C++ 学习笔记 —— C++面向对象编程
- Modern C++ 学习笔记 —— C++函数式编程
Modern C++ 智能指针篇
关键字:智能指针、shared_ptr、unique_ptr
RAII
RAII,资源获取即初始化(Resource Acquisition Is Initialization),是一种C++编程中的惯用手法。但是不要被它的名字给误导了,它实际上的作用是将资源的生命周期与一个对象的生命周期相绑定。所谓的资源包括但不限于分配的堆内存、打开的套接字、打开的文件、锁定的互斥体等。
RAII 保证资源可用于任何会访问该对象的函数。它亦保证所有资源在其控制对象的生存期结束时,以获取顺序的逆序释放。类似地,若资源获取失败(构造函数以异常退出),则为已构造完成的对象和基类子对象所获取的所有资源,会以初始化顺序的逆序释放。这有效地利用了语言特性(对象生存期、退出作用域、初始化顺序以及栈回溯)以消除内存泄漏并保证异常安全。
RAII 可总结如下:
- 将每个资源封装入一个类,其中
- 构造函数请求资源,并建立所有类不变式,或在它无法完成时抛出异常,
- 析构函数释放资源并决不抛出异常;
- 始终经由 RAII 类的实例使用满足要求的资源,该资源
- 自身拥有自动存储期或临时生存期,或
- 具有与自动或临时对象的生存期绑定的生存期
C++标准库提供的用以管理用户提供的资源的包装器实际上也是RAII手法,比如要大费周章深入学习的std::unique_ptr和std::shared_ptr。
自己动手实现智能指针
相比于使用std::unique_ptr管理具备专属所有权的资源,使用std::shared_ptr管理共享所有权的资源要更加常见一些。
unique_ptr和shared_ptr的主要区别如图下所示:
多个shared_ptr不仅可以共享一个对象,同事共享一个技术。当最后一个指向对象(和共享计数)的shared_ptr析构时,它需要删除对象和共享计数。
首先实现共享计数的接口:
class SharedCount {
public:
SharedCount() : count(1) {
}
void AddCount() {
++count; }
long ReduceCount() {
return --count;}
long RetCount() const {
return count;}
private:
long count;
};
现在我们可以实现我们的引用计数智能指针了。首先是构造函数、析构函数、和私有成员变量:
template <typename T>
class SharedPtr {
public:
explicit SharedPtr(T *tmpPtr = nullptr) : ptr(tmpPtr)
{
if (ptr) {
sharedCount = new SharedCount();
}
}
~SharedPtr()
{
if (ptr && !sharedCount->ReduceCount()) {
// RAII
delete ptr;