介绍
在使用智能指针之前,一般都是用new和delete来进行内存的管理,但是一旦忘记delete,这样容易造成内存泄露。从而导致智能指针的出现,它只需要你创建,之后就不需要你管,也不需要你手动释放,到该释放的地方,它会自动释放。
- 原理:其实智能智能是一个模板类,内部有指向模板的一个指针,释放使用析构函数进行,所以这是自动的过程。定义在<memory> 命名空间 std
四种智能指针
std::auto_ptr(已经被C++11弃用)
使用的时候会给警告:is deprecated
- 注意:不建议使用,但是如果使用,记住它是独占式,将p2 = p1 ,那么p1 则是nullptr,如果再通过p1去操作,程序会直接崩溃
std::unique_ptr
- 介绍:正如名字一样,它也是独占式的指针,尝试将它复制给其他指针会编译报错,但是可以通过std::move()进行操作,操作之后原先的指针也不能用,这也解释了unique的含义。
std::unique_ptr<Person> p1(new Person(1));
int id = p1->GetId();
// std::unique_ptr<Person> p2 = p1; 报错
std::unique_ptr<Person> p2 = std::move(p1);
id = p2->GetId();
cout << id << endl;
// id = p1->GetId(); p1交给p2之后,便不能用了
补充:boost库的 boost::scoped_ptr 也是一个独占式的智能指针,但是它不允许转移所有权,从始而终都只对一个资源负责,它更安全谨慎,但是应用的范围也更狭窄
std::shared_ptr
- 介绍:shared_ptr 是一种共享式所有权的指针,它允许多个指针指向同一个对象,既然它提供多个指针指向同一个对象的功能,但是到底由谁来释放?我们想的肯定是由最后一个指针来进行释放工作,如何知道当前的已经是最后一个指针了?所以引入了额外开销:计数
- shared_ptr 除了拥有一个指向目标对象的指针外,还有一个指向 “计数区域(里面有计数器)” 的指针, 结构大概这样:
当每次进行复制shared_ptr的时候,通过ptr2间接的操作counter+1,某处shared_ptr“释放”的时候,counter-1,直到0,才去真正释放object对象和counter区域
{
std::shared_ptr<Person> p1(new Person(1)); // counter:1
int id = 1;
while(id--)
{
std::shared_ptr<Person> p2 = p1; //counter:2
} // counter:1
std::shared_ptr<Person> p3 = p1; // counter:2
} // counter:0
缺陷:存在循环引用计数不正常(采用weak_ptr可以解决此问题),详细见参考1,2
std::weak_ptr
- weak_ptr 是为了辅助shared_ptr而存在,在上面shared_ptr 前提条件下,在counter里面加入weak_counter计数器,所以有了两个计数器(shared_counter, weak_counter)
- weak_ptr存在的机制依赖于以下几个条件:
- weak_ptr 可由另一个weak_ptr或者shared_ptr构造
- weak_ptr的构造和析构不会引起shared_count的增加或减少,只会引起weak_count的增加或减少。
- 被管理资源的释放只取决于shared_counter计数,当shared_counter计数为0,才会释放被管理资源。即weak_ptr不控制资源的生命周期
- 计数区域的释放却取决于shared计数和weak计数,当两者均为0时,才会释放计数区域
- 可以利用weak_ptr的成员函数use_count() 来查看share_counter的计数值
int main(void)
{
std::shared_ptr<Person> p1(new Person(1)); // counter:1
std::weak_ptr<Person> pp1 = p1;
cout << pp1.use_count() << endl;
int id = 1;
while(id--)
{
std::shared_ptr<Person> p2 = p1; //counter:2
cout << pp1.use_count() << endl;
} // counter:1
cout << pp1.use_count() << endl;
std::shared_ptr<Person> p3 = p1; // counter:2
cout << pp1.use_count() << endl;
return 0;
} // counter:0
执行结果:
- weak_ptr 没有重载 -> 和* 符号,所以没有办法进行操作,但是可以使用lock()获得一个可用的shared_ptr, 如果没有可用的shared_ptr,返回的是空指针。
std::shared_ptr<Person> p1(new Person(1));
std::weak_ptr<Person> p2 = p1;
//id = p2->GetId(); // 错误,weak_ptr 没有重载->和*
std::shared_ptr<Person> p3 = p2.lock();
int id = p3->GetId();
cout << id << endl;
- weak_ptr 可以打破shared_ptr的循环引用计数错误的问题,见参考1,2
如何选取智能指针
- 如果需要多个指针指向同一个对象,使用shared_ptr
- 如果某一个对象规定只有唯一的指针来进行操作,使用unique_ptr