C++11引入了三个智能指针,它们分别是如下的三个:

  • std::shared_ptr<T>
  • std::unique_ptr<T>
  • std::weak_ptr<T>

同时C++98的智能指针auto_ptr已经被废弃使用。

std::shared_ptr

std::shared_ptr是共享资源所有权的指针,它对资源作引用计数,当引用计数为0时,自动的释放内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。

以下需要注意

  • 智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。我们也可以使用make_shared函数初始化,但是不能将指针直接赋值给一个智能指针,一个是类,一个是指针。
    1
    2
    3
    4
    5
    #include<memory>
    int main() {
    std::shared_ptr<int> sptr = new int(10); // bad
    return 0;
    }
  • 拷贝和赋值。拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1,当计数为0时,自动释放内存。后来指向的对象引用计数加1,指向后来的对象。
  • get函数获取原始指针。
  • 注意不要用一个原始指针初始化多个shared_ptr指针,否则会造成内存的二次释放。
  • 避免循环引用,从而造成内存的二次释放。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
#include<memory>
using namespace std;
int main() {
int a = 10;
shared_ptr<int> p1 = make_shared<int>(a); // count = 1
shared_ptr<int> p2(p1); // 复制 count = 2

int b = 20;
int * pb = &b;
// shared_ptr<int> p3 = pb; 错误
shared_ptr<int> p3 = make_shared<int>(b);
p2 = p3; // p3 count = 2; p2 count = 1

}

一些shared_ptr独有的操作

1
2
3
p.use_count() // 返回与p共享对象的智能指针数量,主要用于调试,可能很慢
p.unique() // 若 p.use_count() 为1,返回 true;否则返回false
p.get() // 获取原始指针

因为make_shared是精心为异常安全实现的,建议使用make_shared而不是直接调用的shared_ptr构造函数。
如果不使用make_shared,则必须先使用显式new表达式来创建对象,然后才能将其传递到shared_ptr构造函数。

1
shared_ptr<int> sp2(new int(20));

std::unique_str

std::unique_str是独占资源所有权的指针,同一个时刻只能有一个unique_std指向给定对象。它不能复制到另一个,按值传递给函数,也不能在需要创建副本的任何C++标准库库中使用。我们只能够移动std::unique_str指针,不能简单的复制和拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<memory>
#include<iostream>
using namespace std;
int main() {
{
unique_ptr<int> uptr(new int(10));
//unique_ptr<int> uptr2 = uptr; 禁止赋值
// unqiue_ptr<int> uptr2(uptr) 禁止拷贝
unique_ptr<int> uptr2 = move(uptr);
uptr2.release();
}
// 离开作用域,释放内存
return 0;
}

示例

如何创建unique_ptr实例并在函数间传递这些实例。

1
2
3
4
5
6
7
8
9
10
11
12
unique_ptr<Song> songFactory(const std::wstring& artist, const std::wstring& title) {
return make_unique<Song>(artist, title);
}

vod makeSongs() {
// 建议使用 make_unique 创建 unique_ptr 指针
auto song = make_unique<Song>(L"Mr Tom", L"Sing a Song");
vector<wstring> titles = {song->tilte};
// 转移所有权
unique_ptr<Song> uptr2 = std::move(song);
auto uptr3 = songFactory(L"Mr Jack", L"Love");
}

因为make_unique是精心为异常安全实现的,建议使用make_unique而不是直接调用的unique_ptr构造函数。

std::weak_ptr

std::weak_ptr是共享资源的观察者,需要和std:shared_ptr一起使用,不影响资源的生命周期。std::weak_ptr可以从一个std::shared_ptr或者另一个std::weak_ptr对象构造,获得资源的观测权。但std::weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。

使用std::weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是std::shared_ptr的管理的资源)已经不复存在。

weak_ptr可以使用一个非常重要的成员函数lock()从被观测的std::shared_ptr获得一个可用的std::shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的std::shared_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <memory>

int main() {
{
std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
std::cout << sh_ptr.use_count() << std::endl; // 1

std::weak_ptr<int> wp(sh_ptr);
std::cout << wp.use_count() << std::endl; // 1

if(!wp.expired()){
std::shared_ptr<int> sh_ptr2 = wp.lock(); //get another shared_ptr
*sh_ptr = 100;
std::cout << wp.use_count() << std::endl; // 2
}
}
//delete memory
}