undefined

一、智能指针

auto_ptr:拷贝时转移指针的所有权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template <typename T>
class smart_ptr {
smart_ptr (smart_ptr& other) {
ptr_ = other.release();
}
smart_ptr& operator=(smart_ptr& rhs) {
smart_ptr(rhs).swap(*this);
return *this;
}
T* release() {
T* ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(smart_ptr& rhs) {
using std::swap;
swap(ptr_, rhs.ptr_);
}
};

上述代码中这种惯用法保证了强异常安全性:赋值分为拷贝构造和交换两步,异常只可能在第一步发生;而第一步如果发生异常的话,this 对象完全不受任何影响。无论拷贝构造成功与否,结果只有赋值成功和赋值没有效果两种状态,而不会发生因为赋值破坏了当前对象这种场景。

不过 auto_ptr 将它传递给另一个对象之后,就不再拥有这个对象了。

unique_ptr:移动指针

1
2
3
4
5
6
7
8
9
10
template <typename T> 
class smart_ptr {
smart_ptr (smart_ptr&& other) {
ptr_ = other.release();
}
smart_ptr& operator=(smart_ptr&& rhs) {
rhs.swap(*this);
return *this;
}
};

必须以右值的形式传入,明确告诉用户对象已经转移

shared_ptr

通过引用计数的方式,多个智能指针可以拥有一个对象。存在循环引用的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
template <typename T>
class Node {
public:
std::shared_ptr<Node<T>> pre_;
std::shared_ptr<Node<T>> next_;
T value_;

Node(const T& value) : value_(value) {
std::cout << "Node(const T& value)" << std::endl;
}

~Node() {
std::cout << "~Node()" << std::endl;
std::cout << "this:" << this << std::endl;
}
};

void Funtest() {
std::shared_ptr<Node<int>> sp1(new Node<int>(1));
std::shared_ptr<Node<int>> sp2(new Node<int>(2));

std::cout << "sp1.use_count: " << sp1.use_count() << std::endl;
std::cout << "sp2.use_count: " << sp2.use_count() << std::endl;

sp1->next_ = sp2;
sp2->pre_ = sp1;

std::cout << "sp1.use_count: " << sp1.use_count() << std::endl;
std::cout << "sp2.use_count: " << sp2.use_count() << std::endl;
}
// 输出
Node(const T& value)
Node(const T& value)
sp1.use_count: 1
sp2.use_count: 1
sp1.use_count: 2
sp2.use_count: 2

没有调用析构函数,导致内部内存泄露

循环引用:shared_ptr 是一组指针指向一个实例,有几个 shared_ptr 指向这个类,那么这个类就有几次引用。如下图

weak_ptr

weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。从这个角度看,weak_ptr更像是shared_ptr的一个助手而不是智能指针。

初始化方式

  1. 通过shared_ptr直接初始化,也可以通过隐式转换来构造
  2. 允许移动构造,也允许拷贝构造

需要注意,weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数

  • 如何判断weak_ptr指向对象是否存在?

既然weak_ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,就不能使用weak_ptr直接访问对象。那么如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr(引用计数会增1),否则返回一个空shared_ptr。weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁

由于weak_ptr并没有重载operator ->和operator *操作符,因此不可直接通过weak_ptr使用对象,同时也没有提供get函数直接获取裸指针。典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。

二、线程安全

智能指针包括一个实际数据指针和一个引用计数指针,这两个操作不是一个指令可以完成的。因此多线程情况下是有问题的

根据boost官方文档 shared_ptr_thread_safety有如下结论:

  1. 同一个shared_ptr被多个线程读,是线程安全的;
  2. 同一个shared_ptr被多个线程写、或被多个线程读写,不是线程安全的;(析构算写操作)
  3. 共享引用计数的不同的shared_ptr被多个线程写,是线程安全的。