1、定义
智能指针(RAII-Resource Acquisition Is Initialization),资源获取即初始化机制对普通指针的一种封装,这样使得智能指针的行为动作像一个指针。本质上却是一个对象,这样可以方便管理一个对象的生命周期。
在c++中智能指针一共定义了四种:unique_ptr、shared_ptr、auto_ptr和weak_ptr。其中,auto_ptr在C++11已被摒弃,在C++17中已经移除不可用。
2、原始指针的问题
当指针未删除,或者删除的情况未考虑清除,容易造成悬挂指针(danging pointer)和野指针(wild pointer)的现象
注:悬挂指针(野指针):指针指向非法的内存地址,原因一般有
a、使用未初始化的指针
b、指针所指的对象已消亡
c、指针释放后未被置空
因此,使用普通指针我们经常会遇到各种问题,需要手动初始化和释放,智能指针可以有效避免这些问题。
3、unique_ptr
初始化:有四种构造方式,unique_ptr和shared_ptr两者的构造函数声明均为explicit,即不允许隐式类型转换。unique_ptr不允许拷贝构造
①unique_ptr<T> up;up.reset(new T());
②unique_ptr<T> up(new T());
③unique_ptr<T> up1 = std::move(up);//移动构造
④auto up = make_unique<int>(10);
struct Task {
int mId;
Task(int id) :mId(id)
{
std::cout << "Task::constructor" << std::endl;
}
~Task()
{
std::cout << "Task::Destructor" << std::endl;
}
};
int main()
{
unique_ptr<Task> taskPtr(new Task(23));
return 0;
}
输出结果:
Task::constructor
Task::Destructor
不管函数正常退出还是异常退出,也始终会调用taskPtr的析构函数。因此,原始指针将始终被删除并防止内存泄漏。
特性:unique_ptr独享所有权
unique_ptr始终是关联原始指针的唯一所有者,无法复制unique_ptr对象,只能移动。每个unique_ptr都是原始指针的唯一拥有者,因此在其析构函数中直接删除相关联的指针。我们无法复制unique_ptr对象,但可以转移他们。
int main()
{
unique_ptr<Task> taskPtr(new Task(23));
unique_ptr<Task> taskPtr4 = std::move(taskPtr);
if (taskPtr == nullptr)
{
std::cout << "data convert success" << std::endl;
}
return 1;
}
输出结果:
Task::constructor
data convert success
Task::Destructor
尽管重新定义了一个新变量taskPtr4,但是只有一次构造和析构。
独有操作:
up.release(); up放弃对指针的控制权,返回裸指针,并将up置空
up.reset(); 释放up指向的对象
up.reset(q):其中q为裸指针,令up指向q所指对象
4、shared_ptr
初始化:
①shared_ptr<T> sp;sp.reset(new T());
②shared_ptr<T> sp(new T());
③shared_ptr<T> sp1 = sp; //拷贝构造
④auto sp = make_shared<int>(10);
独有操作:
①shared_ptr<T> p(q);//拷贝构造
②p = q;//赋值
③p.unique();若p.use_count()为1,返回true,否则返回false。
④p.use_count()//返回强引用计数
shared_ptr<Task> sharePtr(new Task(25));
std::cout << sharePtr.use_count() << endl;
shared_ptr<Task> sharePtr4 = sharePtr;
std::cout << sharePtr.use_count() << endl;
返回结果:
1
2
5、weak_ptr
weak_ptr虽然是智能指针,但实际上作为shared_ptr的辅助指针使用。weak_ptr不会影响shared_ptr对象的引用计数。
成员函数:
- reset()使weak_ptr对象变为空指针,类似默认构造函数。(与shared_ptr,unique_ptr的reset不同,weak_ptr的reset不能带参数)。
- swap(std::weak_ptr<T> &p)交换两个weak_ptr对象的内部指针。
- use_count()查看关联shared_ptr的引用计数。
- expired()判断weak_ptr是否过期。如果weak_ptr为空,或者关联的shared_ptr为空,返回true。
- lock()如果weak_ptr对象已过期,返回一个空shared_ptr对象;否则,返回一个weak_ptr内部指针相关联的shared_ptr对象(因此,shared_ptr对象的引用计数+1)。
使用shared_ptr在循环引用时会出现内存泄漏的情况,由于循环引用,use_count不会在代码块结束时降为0,shared_ptr指向的内存不会被释放。
class A;
class B;
class A
{
public:
std::shared_ptr<B> b_;
public:
A() {
std::cout << "construct A" << std::endl;
}
~A() {
std::cout << "destroy A" << std::endl;
}
};
class B
{
public:
std::shared_ptr<A> a_;
public:
B() {
std::cout << "construct B" << std::endl;
}
~B() {
std::cout << "destroy B" << std::endl;
}
};
结果可以看到析构没有被调用
construct A
construct B
A use_count is 1
B use_count is 1
A use_count is 2
B use_count is 2
使用weak_ptr声明对象成员变量可以避免这种问题
construct A
construct B
A use_count is 1
B use_count is 1
A use_count is 1
B use_count is 1
destroy B
destroy A
参考:
一篇文章说清楚shared_ptr,unique_ptr的区别和关系_燕山暮雪的博客-CSDN博客_unique_ptr
C++ 智能指针 unique_ptr 详解与示例_hhy980205的博客-CSDN博客_c++ unique_ptr