摘要:C++如何正确使用智能指针?看完这4个点你就明白了康熙来了20120713
C++11 中推出了三种智能指针,unique_ptr、shared_ptr 和 weak_ptr,同时也将 auto_ptr 置为废弃(deprecated)。
但是在实际的使用过程中,很多人都会有这样的问题:
- 不知道三种智能指针的具体使用场景无脑只使用 shared_ptr认为应该禁用 raw pointer(裸指针,即 Widget*这种形式),全部使用智能指针
本文试图理清楚三种智能指针的具体使用场景,并讲解三种智能指针背后的性能消耗。
C/C++
对象所有权首先需要理清楚的概念就是对象所有权的概念。所有权在 rust 语言中非常严格,写 rust 的时候必须要清楚自己创建的每个对象的所有权。
但是 C++比较自由,似乎我们不需要明白对象的所有权,写的代码也能正常运行。但是明白了对象所有权,我们才可以正确管理好对象生命周期和内存问题。
C++引入了智能指针,也是为了更好的描述对象所有权,简化内存管理,从而大大减少我们 C++内存管理方面的犯错机会。
unique_ptr:专属所有权我们大多数场景下用到的应该都是 unique_ptr。unique_ptr 代表的是专属所有权,即由 unique_ptr 管理的内存,只能被一个对象持有。所以,unique_ptr 不支持复制和赋值,如下:
auto w = std::make_unique<Widget();auto w2 = w; // 编译错误
如果想要把 w 复制给 w2, 是不可以的。因为复制从语义上来说,两个对象将共享同一块内存。
因此,unique_ptr 只支持移动, 即如下:
auto w = std::make_unique<Widget();auto w2 = std::move(w); // w2获得内存所有权,w此时等于nullptr
unique_ptr 代表的是专属所有权,如果想要把一个 unique_ptr 的内存交给另外一个 unique_ptr 对象管理。只能使用 std::move 转移当前对象的所有权。转移之后,当前对象不再持有此内存,新的对象将获得专属所有权。
如上代码中,将 w 对象的所有权转移给 w2 后,w 此时等于 nullptr,而 w2 获得了专属所有权。
性能
因为 C++的 zero cost abstraction 的特点,unique_ptr 在默认情况下和裸指针的大小是一样的。所以内存上没有任何的额外消耗,性能是最优的。
使用场景 1:忘记 delete
unique_ptr 一个最简单的使用场景是用于类属性。代码如下:
class Box{public: Box() : w(new Widget()) {} ~Box() { // 忘记delete w }private: Widget* w;};
如果因为一些原因,w 必须建立在堆上。如果用裸指针管理 w,那么需要在析构函数中delete w; 这种写法虽然没什么问题,但是容易漏写 delete 语句,造成内存泄漏。
如果按照 unique_ptr 的写法,不用在析构函数手动 delete 属性,当对象析构时,属性w将会自动释放内存。