shared_ptr in C++11

shared_ptr

What is std::shared_ptr<>

shared_ptr是c++11提出的一种智能指针类,能够自动地删除掉相关的不再被使用的指针,它能够帮助解决内存泄漏和悬空指针的问题。

shared_ptr有一个共享对象的概念,不同的shared_ptr可以共享相同的指针,并且通过内部的引用计数机制来实现这一功能。每个shared_ptr内部都指向两个内存区域,一个是指向对象的指针,另一个就是用来做引用计数的数据。

引用计数的使用方式:

  • 当有一个新的shared_ptr与指针相关联后,其引用计数递增1;
  • 当一个shared_ptr对象离开作用域时,其引用计数递减1。并且在引用计数变为0的时候,它会delete那部分内存;

Creating a shared_ptr Object

创建shared_ptr对象时需要绑定一个原生指针,如下:

1
std::shared_ptr<int> p1(new int());

这样,就在堆上创建了两块内存:一个是int,一个是引用计数的数据区域。

至于,要查看目前的引用计数是多少:

1
p1.use_count();

另外,要将一个指针赋值给shared_ptr,我们不能采用隐式的方式,因为其构造器是采用explicit的方式,所以隐式赋值会报错,但我们可以使用std::make_shared

1
2
std::shared_ptr<int> p1 = new int(); // Compile error
std::shared_ptr<int> p1 = std::make_shared<int>();

Detaching the associated Raw Pointer

要使shared_ptr对象取消附加其附加指针,可以调用reset()方法。

  • 无参调用reset:
1
p1.reset(); // 递减引用计数
  • 有参调用:
1
p1.reset(new int(34)); //指向新的指针,引用计数变为1
  • 使用nullptr reset
1
p1 = nullptr;

shared_ptr可以看作是普通指针,即我们可以对shared_ptr对象使用*和->与,也可以像其他shared_ptr对象一样进行比较。

shared_ptr and Custom Deletor

在上文说过,shared_ptr对象超出作用域的时候,会递减引用计数,当计数为0时,默认情况下会调用delete函数删除指针。但如果我们的shared_ptr指向的是一个数组,就应该使用delete[]了。

因此,为了避免默认调用的错误,我们可以自定义删除器。

1
2
3
4
5
6
void deleter(Sample * x)
{
std::cout << "DELETER FUNCTION CALLED\n";
delete[] x;
}
std::shared_ptr<Sample> p3(new Sample[12], deleter);

当然也可以利用lambda函数或者函数对象来自定义删除器。

shared_ptr vs Pointer

与原生指针不同,shared_ptr只有以下的的操作符:

  • ->, *, 比较符号;

不提供原生指针的这些操作:

  • +, -, ++, —和[];

当我们创建shared_ptr对象而不分配任何值时,它就是空的。而对于原生的指针来说,它会包含一个垃圾值。因此对于shared_ptr对象,我们可以这样检查:

1
2
3
4
5
6
7
std::shared_ptr<Sample> ptr3;
if(!ptr3)
std::cout<<"Yes, ptr3 is empty" << std::endl;
if(ptr3 == NULL)
std::cout<<"ptr3 is empty" << std::endl;
if(ptr3 == nullptr)
std::cout<<"ptr3 is empty" << std::endl;

Create shared_ptr objects carefully

在创建shared_ptr对象时,有些情况需要注意的:

  1. 不要使用相同的原始指针来创建多个shared_ptr对象,因为不同的shared_ptr对象并不知道它们正在与其它shared_ptr对象共享指针;
  2. 不要从stack中创建shared_ptr对象,因为在stack内存上调用删除操作,程序会崩溃。因此我们应该使用make_shared<>之类的;
1
2
3
std::shared_ptr<int> ptr_1 = make_shared<int>();

std::shared_ptr<int> ptr_2 (ptr_1);