C++智能指针几条建议(一)

程序员在C语言的领地里,常常如履薄冰,而在内存管理方面,如果经验不足,简直就是地狱——对于开发者和维护者都是。在C++的领地里,由于RAII的存在,我们貌似可以高枕无忧,然而事实并非如此,情况貌似更加混乱了——因为我们又引入了一些看不见自动的东西:构造函数和析构函数的自动机制。如果你懂得RAII并且小心应对,你基本可以写出漂亮的程序,然而当逻辑持续复杂,你会发现我们需要GC,但是C++没有GC,不过好在我们有智能指针——伟大的发明。

智能指针与线程安全

智能指针本身不是100%线程安全的

智能指针的线程安全级别几乎和内建类型一样,他不是线程安全的,多个线程可以一起”读“,但是一旦有一个线程进行”写“操作,未加锁的情况下,其他线程的操作都是不安全的。接下来我们会讨论各种智能指针(unique_ptr、shared_ptr、weak_ptr),不过在此之前我们需要先明确一下,对于一个智能指针,那些操作是”写“那些是”读“,如下表所示:

操作 unique_ptr shared_ptr weak_ptr
constructor W W W
copy constructor - W W
destructor W W W
operator = W W W
operator */-> R R R
operator bool R R -
get_deleter() R - -
reset() W W W
swap() W W W
lock() - - W

注: W 表示写操作 R 表示读操作 - 表示没有该操作

为了说明简便,没有列出全部的智能指针支持的操作

unique_ptr

首先unique_ptr有两个成员,一个是指向所拥有对象的原始指针,一个是保存的deleter,如下图所示:
unique_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
class BigSlowResource;
class ResourceFactory {
std::unordered_map<int, std::weak_ptr<BigSlowResource> > cache;
public:
std::shared_ptr<BigSlowResource> makeBigSlowResource(int ResID) {
auto ptr = cache[ResID].lock();
if (!ptr) {
ptr = std::shared_ptr<BigSlowResource>(new BigSlowResource(ResID));
cache[ResID] = ptr;
}
return ptr;
}
};
void thread_one(std::unique_ptr<BigSlowResource>& pa) {
if (!pa) {
pa.reset(new BigSlowResource());
}
// process with BigSlowResource
}
void thread_two(std::unique_ptr<BigSlowResource>& pa) {
if (!pa) {
pa.reset(new BigSlowResource());
}
// another process with BigSlowResource
}

智能指针引用的对象不是线程安全的