C++ Smart Pointer(智能指针)

早在C++98/03标准的时候,C++就引入了智能指针(Smart Pointer)的概念来帮助程序员对于堆内存进行自动回收。

引入的目地主要是:对指针的行为进行一定的规范。这有很多的好处,比如:

  • 这样可以避免野指针的出现
  • 这样可以避免错误的对于同一块地址进行两次释放
  • 释放不在使用的内存资源,避免内存的泄漏.
    智能指针使得为我们能够更加安全有效的使用(共享)同一块堆空间.

shared_ptr的实现

C++ 智能指针是采用的 引用计数(Reference counting) 的方式实现的。更简单的说,智能指针会负责维护一个整形值用于标记当前空间的引用值。当有新的对象使用同一块堆内存时,整形值加一。当使用该块堆内存的对象被释放掉时,整形值减一。当这个值减少为0时,则表示除了不在会有对象使用这块内存,该堆空间会被释放掉(而不用程序员担心什么时候释放)。

1
2
3
// std::shared_ptr采用的是模板类的方式定义的,它继承自std::_shared_ptr 这个类(shared_ptr_base.h文件中).
template<typename _Tp>
class shared_ptr : public __shared_ptr<_Tp>

shared_ptr继承自_shared_ptr,在这个类中维持了两个对象,一个是element_type类型的指针_M_ptr,一个是__weak_count<_Lp>类型的计数器_M_refcount。通过对这两个对象的维护,实现智能指针的目的.
例如,use_count()函数借助_M_refcount直接返回的引用数。

1
2
3
long use_count() const noexcept{ 
return _M_refcount._M_get_use_count();
}

如何使用智能指针

C++11中提供了三种智能指针:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用时需添加头文件

C++98/03 标准中,曾经支持使用 auto_ptr 智能指针来实现堆内存的自动回收,但后来在C++17被废除.

下面以shared_ptr为例,介绍C++中智能指针的使用.

shared_ptr的构造方法

shared_ptr 这个类为我们提供了十几种创建共享指针的构造方法.

1
2
3
4
5
6
7
8
9
10
11
12
// 我们能够通过以下的方式来创建 shared_ptr 指针
std::shared_ptr<int> p1;
std::shared_ptr<int> p1(nullptr);
std::shared_ptr<int> p1(new int[10]);
//我们还可以借助make_shared<T>函数
std::shared_ptr<int> p3 = std::make_shared<int>(10);
//拷贝构造函数
std::shared_ptr<int> p4(p3);
>>运行之后,计数加1,p3 和 p4 指向同一块堆内存.
//移动构造函数
std::shared_ptr<int> p5(std::move(p4));
>运行之后,计数不变p5变为指向内存的智能指针,p4变成空智能指针

shared_ptr智能指针的释放和内存的释放

以下的两种情况会导致内存的释放:

  • 最后一个拥有该块堆内存的shared_ptr指针被销毁时.
  • 最后一个拥有该对象的shared_ptr通过操作符=或reset()被分配另一个指针时。

智能指针除了为我们提供了默认的内存释放规则,还允许我们自定义内存释放的规则.

1
2
3
4
5
6
7
8
9
10
//指定 default_delete 作为释放规则
std::shared_ptr<int> p6(new int[10], std::default_delete<int[]>());
//自定义释放规则
void deleteInt(int*p) {
delete []p;
}
//初始化智能指针,并自定义释放规则
std::shared_ptr<int> p7(new int[10], deleteInt);

std::shared_ptr<int> p8(new int[10],[](int* p){delete []p; })

其他方法

shared_ptr 是继承的__shared_ptr 这个类.
这个基类为我们提供了以下的一些方法:

方法名 说明 返回类型
get() 获得当前智能智能指向元素的普通指针 element_type*
user_count() 当前堆内存空间的引用量 long
unique() 当前堆内存是否还有其他引用 bool
swap() 交换两个智能指针 void
reset() 没有参数时,当前智能指针变为空智能指针,有参数时当前智能指针指向给定的新内存空间,原内存引用值减1,当前内存引用值置1 void

shared_ptr,unique_ptr, std::weak_ptr之间的区别

shared_ptr能够实现多个shared_ptr类型的指针“共享”使用同一块堆内存,当有一个指针放弃该堆内存的“使用权”时,也不会影响其他指向该堆内存的shared_ptr空间.只有当引用计算变为0时,才会释放掉这一块堆内存空间.

unique_ptr 通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象的智能指针。、

std::weak_ptr 用来表示对于std::shared_ptr 管理的对象的弱引用. 用来表达临时所有权的概念,当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,std::weak_ptr 用来跟踪该对象。此外值得注意的是:weak_ptr 类型指针并不会影响所指堆内存空间的引用计数。

参考

http://c.biancheng.net/view/7898.html
https://en.cppreference.com/w/cpp/memory/shared_ptr