智能指针(smart pointer)是一种抽象的数据类型(abstract data type)。 在程序设计中, 智能指针通常由类模板(class template)实现,。 通常借助模板达到泛型,借助类的析构函数来达成自动释放指针所指向的内存或者对象。
智能指针模拟了指针的所有的属性, 然后在加上指针没有的额外的features, 这些features包括自动内存管理(automatic memory mangement), 或者bouds checking。 之所以加上这些特性, 是因为这些额外的特性具有很多好的作用, 能够减少因为使用指针不当而造成的bugs, 并且保持高效型。
Misuse of pointers通常是造成bugs的主要的源头所在。 使用智能指针的自动内存回收机制(automatic memory deallocation)能够避免出现内存泄露(memory leaks)。 更一般的, 他们能够进行对象的自动析构:当对象的最后一个owner(即智能指针)被destroyed的时候, 智能指针的控制着(或者拥有的)的对象就可以自动被destroyed。 举个例子, 如果对象的owner(智能指针)是一个local variable的时候, 当execution运行到智能指针的scope之外的时候, 那么智能指针指向的对象也能够自动destroyed了。 智能指针通过推迟对象的destruction, 直至object不再被使用, 才进行对象的析构, 这样就不会再有dangling pointers了。 这也就不需要我们手动的去delete了。
智能指针有很多种:
(1) 一些智能指针有reference counting(引用计数)的机制(shared_ptr)。 这很节省内存。 Linux里面管理文件Inode就是采用的引用计数的机制。
(2)一些智能指针通过对ownership of an object进行赋值(unique_ptr)。 这个对象只能一次被一个指针指向。
C++的智能指针是通过模板了类实现的。 利用operator overloading的方式, 这样智能指针就具有了除了原始指针(raw pointer)的一些behavior(例如dereference, assignment)之外, 还提供额外的内存管理特征(memory management features)。
C++11中提供了std::unique_ptr, 定义在<memory>头文件中。 std::unique_ptr替代了原先的std::auto_ptr, 也就是auto_ptr是deprecated, 基本上现在最后不用auto_ptr。
C++11新增了move语义。 相比于copy语义, move能更好的实现值传递。 std::auto_ptr使用的是copy语义。 为了先前兼容, C++11没有修改std::auto_ptr, 而是引入了新的使用move语义的std::unique_ptr。
auto_ptr(现在已经不再用了)不能作为容器(例如vector)的元素, 而unique_ptr可以作为容器的元素。
unique_ptr的拷贝构造函数和赋值运算符都声明为deleted。 也就是说它不能被拷贝, 只能通过std::move来传递它指向的内存的所有权。 下面的例子说的很清楚:
<span style="font-size:14px;">std::unique_ptr<int> p1(new int(5)); // new int(5)在heap中分配一个能存一个int的内存, p1指向这个int的对象
std::unique_ptr<int> p2 = p1; // 编译会出错, 即对unique_ptr不能拷贝构造和赋值, 因为赋值运算符被声明ideleted
std::unique_ptr<int> p3 = std::move(p1); // move了, 转移所有权, 现在那块内存归p3所有, p1成为无效的指针.
p3.reset(); //释放内存.
p1.reset(); //实际上什么都没做.</span>
使用unique_ptr的一个例子:
<span style="font-size:14px;">#include <iostream>
#include <memory>
struct Foo {
Foo() { std::cout << "Foo::Foo\n"; }
~Foo() { std::cout << "Foo::~Foo\n"; }
void bar() { std::cout << "Foo::bar\n"; }
};
void f(const Foo &foo)
{
std::cout << "f(const Foo&)\n";
}
int main()
{
std::unique_ptr<Foo> p1(new Foo); // p1 owns Foo
if (p1) p1->bar();
{
std::unique_ptr<Foo> p2(std::move(p1)); // now p2 owns Foo, p1对对象没有ownership
f(*p2);
p1 = std::move(p2); // ownership returns to p1
std::cout << "destroying p2...\n";
}
if (p1) p1->bar();
// Foo instance is destroyed when p1 goes out of scope
}</span>
运行结果如下:
shared_ptr和weak_ptr
定义在头文件<memory>中。
基于Boost库, C++引入了另外两个智能shared_ptr和weak_ptr。 他们最早在TR1中引入, 但是在C++11中, 在Boost的基础上又加入了新的功能。
std::shared_ptr使用的是引用计数(reference counting)。 每一个shared_ptr的拷贝都指向相同的内存。 在最后一个shared_ptr析构的时候, 内存才会被释放(当引用计数变成0了)。
<span style="font-size:14px;">std::shared_ptr<int> p1(new int(5));
std::shared_ptr<int> p2 = p1; // 都指向同一内存.
p1.reset(); // 因为p2还在,所以内存没有释放. 也就是说int(5)还存在着
p2.reset(); // 释放内存, 因为没有shared_ptr指向那块内存了(即reset后, 引用计数变成0了). 即int(5)那块内存被回收了</span>
关于shared_ptr的reset函数:
原型:
void reset();
|
(1) | (since C++11) |
template< class Y > void reset( Y* ptr ); |
(2) | (since C++11) |
template< class Y, class Deleter > void reset( Y* ptr, Deleter d ); |
(3) | (since C++11) |
template< class Y, class Deleter, class Alloc > void reset( Y* ptr, Deleter d, Alloc alloc ); |
(4) | (since C++11) |
For signature (1) the object becomes empty (as if default-constructed).
In all other cases, the shared_ptr acquires ownership of p with a use count of 1, and -optionally- with del and/oralloc as deleter and allocator, respectively.
Additionally, a call to this function has the same side effects as if shared_ptr's destructor was called before its value changed (including the deletion