0/# 一、为什么要有智能指针
内存泄露是我们开发大型项目时最为头疼的问题,当我们将对象建立在堆上时,因为需要我们自己手动释放,因此避免不了忘记删除,或者删除时没有考虑清楚情况的问题,从而造成悬挂指针或者是野指针的问题。
二、智能指针是什么
简单理解的话,智能指针采用RAII机制。即虽然智能指针虽然是以指针的方式运作,但是实际上是一个对象,在自己生命周期结束后,会自动释放掉,这样的话就不用让开发人员时刻把心思放在释放对象这个问题上了,也降低了内存泄漏的概率。
在C++中,智能指针一共定义了4种:
auto_ptr、unique_ptr、shared_ptr 和 weak_ptr。 其中,auto_ptr在C++11种已经摒弃掉,在C++17中已经废除不可用。
三、智能指针
1、unique_ptr
指针p在运行完test()函数后会直接释放掉。
需要注意的是,unique_ptr并没有负责复制的构造函数,因此不支持拷贝和赋值操作。原因是unique_ptr会独享p1的所有权,如果p2和p3失去p1的所有权时(即p1释放掉)会delete p1两次,p1释放一次,p2释放会再次delete p1一次。
虽然不能够拷贝和赋值操作,但是可以将p1的所有权转移给p2管理,使用std::move函数即可。
unique最常见的使用场景,就是替代原始指针,为动态申请的资源提供异常安全保证。
#include<iostream>
#include<vector>
using namespace std;
class Test
{
public:
void add();
};
void Test::add()
{
;
}
int main()
{
//情况一
Test* t = new Test();
t->add();
delete t;
//情况二
unique_ptr<Test> t(new Test);
t->add();
delete t;
}
在这个场景中,如果情况一中 t 执行add()函数,执行过程中出现异常,导致无法执行后面无法释放掉对象。
而情况二中,即使 t 执行add()函数出现异常,无法继续后面delete t 代码时,但对象 t 生命周期结束后,智能指针会自动调用析构函数,释放掉对象。
2、shared_ptr
大部分提到的智能指针很大概率上指的是shared_ptr,它采用的机制是引用计数,即实现对一块内存的多个引用,在最后一个引用被释放时,指向的内存才释放。shared_ptr可以进行赋值和拷贝操作。
shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。
获取原始指针:
注意事项:
- 不能将一个原始指针初始化多个shared_ptr对象(因为会多次删除相同指针)
- 循环引用问题
#include<iostream>
#include<vector>
using namespace std;
struct Father
{
shared_ptr<Son> son_;
};
struct Son
{
shared_ptr<Father> father_;
};
int main()
{
father = make_shared<Father>();
auto son = make_shared<Son>();
father->son_ = son;
son->father_ = father;
return 0;
}
该部分代码会有内存泄漏问题。原因是
1.main 函数退出之前,Father 和 Son 对象的引用计数都是 2。
2.son 指针销毁,这时 Son 对象的引用计数是 1。
3.father 指针销毁,这时 Father 对象的引用计数是 1。
4.由于 Father 对象和 Son 对象的引用计数都是 1,这两个对象都不会被销毁,从而发生内存泄露。
为避免循环引用导致的内存泄露,就需要使用 weak_ptr。weak_ptr 并不拥有其指向的对象,也就是说,让 weak_ptr 指向 shared_ptr 所指向对象,对象的引用计数并不会增加。
使用 weak_ptr 就能解决前面提到的循环引用的问题,方法很简单,只要让 Son 或者 Father 包含的 shared_ptr 改成 weak_ptr 就可以了。
#include<iostream>
#include<vector>
using namespace std;
struct Father
{
shared_ptr<Son> son_;
};
struct Son
{
weak_ptr<Father> father_;
};
int main()
{
shared_ptr<Father> father = make_shared<Father>();
shared_ptr<Son> son = make_shared<Son>();
father->son_ = son;
son->father_ = father;
return 0;
}