智能指针,利用class类对象销毁的时候自动调用析构函数,去把delete ptr的操作放在析构函数里,去实现自动释放指针里的资源
RAII是ResourceAcquisition Is Initialization的缩写,他是⼀种管理资源的类的设计思想,本质是 ⼀种利⽤对象⽣命周期来管理获取到的动态资源,避免资源泄漏,这⾥的资源可以是内存、⽂件指 针、⽹络连接、互斥锁等等。RAII在获取资源时把资源委托给⼀个对象,接着控制对资源的访问, 资源在对象的⽣命周期内始终保持有效,最后在对象析构的时候释放资源,这样保障了资源的正常 释放,避免资源泄漏问题。
智能指针类除了满⾜RAII的设计思路,还要⽅便资源的访问,所以智能指针类还会想迭代器类⼀ 样,重载 operator*/operator->/operator[] 等运算符,⽅便访问资源。
unique_ptr //唯一指针
只有移动,没有拷贝,
shared_ptr //共享指针,引用计数的指针
有移动,有拷贝,引用计数来判断是否对所管理的资源进行delete
weak_ptr //弱指针,解决循环引用内存泄漏问题,配合shared_ptr使用
delete ptr /delete[] ptr; shared和unique的删除器
unique_ptr和shared_ptr都有
unique_ptr<Date[]> up1(new Date[5]);
shared_ptr<Date[]> sp1(new Date[5]);
的用法,
此外,shared的删除器是通过构造函数的第二个参数传入,而unique还要传入模板参数的第二个类型,
struct Date
{
int _year;
int _month;
int _day;
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
~Date()
{
cout << "~Date()" << endl;
}
};
template<class T>
void DeleteArrayFunc(T* ptr)
{
delete[] ptr;
}
template<class T>
class DeleteArray
{
public:
void operator()(T* ptr)
{
delete[] ptr;
}
};
class Fclose
{
public:
void operator()(FILE* ptr)
{
cout << "fclose:" << ptr << endl;
fclose(ptr);
}
};
int main()
{
//这样实现程序会崩溃
// unique_ptr<Date> up1(new Date[10]);
// shared_ptr<Date> sp1(new Date[10]);
// 解决⽅案1
// 因为new[]经常使⽤,所以unique_ptr和shared_ptr
// 实现了⼀个特化版本,这个特化版本析构时⽤的delete[]
unique_ptr<Date[]> up1(new Date[5]);
shared_ptr<Date[]> sp1(new Date[5]);
// 解决⽅案2
// 仿函数对象做删除器
//unique_ptr<Date, DeleteArray<Date>> up2(new Date[5], DeleteArray<Date>());
// unique_ptr和shared_ptr⽀持删除器的⽅式有所不同
// unique_ptr是在类模板参数⽀持的,shared_ptr是构造函数参数⽀持的
// 这⾥没有使⽤相同的⽅式还是挺坑的
// 使⽤仿函数unique_ptr可以不在构造函数传递,因为仿函数类型构造的对象直接就可以调⽤
// 但是下⾯的函数指针和lambda的类型不可以
unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]);
shared_ptr<Date> sp2(new Date[5], DeleteArray<Date>());
// 函数指针做删除器
unique_ptr<Date, void(*)(Date*)> up3(new Date[5], DeleteArrayFunc<Date>);
shared_ptr<Date> sp3(new Date[5], DeleteArrayFunc<Date>);
// lambda表达式做删除器
auto delArrOBJ = [](Date* ptr) {delete[] ptr; };
unique_ptr<Date, decltype(delArrOBJ)> up4(new Date[5], delArrOBJ);
shared_ptr<Date> sp4(new Date[5], delArrOBJ);
// 实现其他资源管理的删除器
shared_ptr<FILE> sp5(fopen("Test.cpp", "r"), Fclose());
shared_ptr<FILE> sp6(fopen("Test.cpp", "r"), [](FILE* ptr) {
cout << "fclose:" << ptr << endl;
fclose(ptr);
});
return 0;
}
shared_ptr的循环引用缺陷
经典例子,你保我,我保你
#include <iostream>
#include <memory>
class Node {
public:
std::shared_ptr<Node> prev; // 指向前一个节点
std::shared_ptr<Node> next; // 指向后一个节点
~Node() {
std::cout << "Node destroyed" << std::endl;
}
};
int main() {
// 创建两个节点,形成循环引用
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2; // node1 引用 node2
node2->prev = node1; // node2 引用 node1
// main函数结束时...
// node1 和 node2 的引用计数都为2(自身+对方)
// 因此它们的析构函数不会被调用,导致内存泄漏
return 0;
}