1.智能指针介绍
智能指针是C++的特性用法,是一个类似指针功能的类对象,其目的是为了更好的管理动态分配的内存,避免出现内存泄漏、悬空指针等问题。C++11的标准库里提供了三种智能指针模板类,分别是std::unique_ptr、std::shared_ptr 和 std::weak_ptr(C++11摒弃了auto_ptr)。
2.为什么需要智能指针
首先来看一段代码
void test(std::string& str)
{std::string* ps = new std::string("nihao!\n");str = *ps;//delete ps;return;
}
不难发现代码的隐患问题,每次函数被调用时,ps指针指向的内存空间都分配在堆上,但是在没有delete ps语句的情况下,无论是函数结束还是异常退出,都无法释放内存。然后我们在面对复杂逻辑的编程时,常常会忘记使用delete,这就会导致内存泄漏。
于是我们就会想,如果在函数终止时(不管是正常终止还是异常终止),new出来的内存都像函数内部的局部变量从栈内存上释放那样,从堆上释放,那就很巴适。智能指针的设计就是解决这样的问题。
来看下面这段代码:
void test_auto(std::string& str)
{std::unique_ptr<std::string> ps(new std::string("nihaoya!\n"));str = *ps;return;
}
使用了智能指针模版类后,因为是通过类来管理我们申请的资源,类的析构函数在类对象出作用域的时候会自动被调用,会自动的清理申请的资源。即使没有delete语句,在函数终止时,指针占据的内存空间和指针指向的内存空间都将被释放。
3. 智能指针用法示例
3.1 独占性智能指针std::unique_ptr
所谓的独占型,即两个unique_ptr不能指向同一个对象,指针直接不能复制,只能将所有权通过move接口转移。
看如下一段代码:
std::unique_ptr<std::string>p1(new std::string("hai\n")); // 1
std::unique_ptr<std::string>p2; // 2
p2 = p1; // 3
p2 = move(p1); // 4
std::cout << *p1 << std::endl; // 5
std::cout << *p2 << std::endl; // 6
其中,第三条p2 = p1;是不被允许的,编译系统提示如下。
第5条std::cout << *p1 << std::endl;由于是经过move后,p1指针的内容为空,运行将产生如下报错。
屏蔽掉报错内容后,系统运行结果如下,将p1指针转移到p2指针,并销毁p1指针内容。
3.2 共享型智能指针std:: shared_ptr
共享指针shared_ptr是只具有共享所有权语义的智能指针。 每当共享指针shared_ptr的最后一个所有者被销毁时,关联对象都将被删除(或关联资源被清除),允许多个指针指向同一个变量。
3.3 弱引用智能指针std:: weak_ptr
weak_ptr专门为了解决std:: shared_ptr循环引用问题而引入的指针类型,通过shared_ptr构造,配合shared_ptr才能使用,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。从这个角度看,weak_ptr更像是shared_ptr的一个助手而不是智能指针。
那么,什么是循环引用的问题呢?在shared_ptr的使用过程中,当强引用计数为0是,就会释放所指向的堆内存。那么问题来了,如果和死锁一样,当两个shared_ptr互相引用,那么它们就永远无法被释放了。
3.4 选择智能指针
如果程序需要使用多个指向同一对象的指针,应选择share_ptr,当程序中存在循环调用的逻辑时,选择weak_ptr指针来辅助,反之若不需要多个指针指向同一个对象,则选择unique_ptr。