视频网站外链怎么做,湖北省住房和城乡建设网站,仿站网站开发,域名申请证书文章标题 智能指针的提出智能指针概念及使用RAII 智能指针的原理C库多种智能指针详解版本一#xff1a;std::auto_ptr#xff08;C98#xff09;1. std::auto_ptr 使用2. std::auto_ptr 原理3. std::auto_ptr 模拟实现 版本二#xff1a;unique_ptr (C11)1. unique_ptr 的使… 文章标题 智能指针的提出智能指针概念及使用RAII 智能指针的原理C库多种智能指针详解版本一std::auto_ptrC981. std::auto_ptr 使用2. std::auto_ptr 原理3. std::auto_ptr 模拟实现 版本二unique_ptr (C11)1. unique_ptr 的使用2. unique_ptr 的原理3. unique_ptr 的模拟实现 版本三 std::shared_ptr(C11) 循环引用问题weak_ptr的简单实现 智能指针的提出
在上篇所将的C异常的文章中有一个这样的场景在new/malloc一个空间到delete/free释放这段资源之间抛异常了抛异常后直接跳到了catch语句导致后面的delete/free语句没有执行到会导致内存泄漏问题。 如下面这个场景
如下代码当main函数调用func函数时func函数内部给ptr开辟了一个空间然后本来是要在func函数结束时将ptr空间释放的但是中间如果抛了异常接直接跳到了catch语句导致ptr空间没有被释放造成内存泄漏。
void func(){int* ptr new int(2); throw(异常);delete ptr;
}
int main()
{try {func();}catch (const char* s){cout s endl;}catch (...){cout 未知异常 endl;}return 0;
}如何避免内存泄漏
工程前期良好的设计规范养成良好的编码规范申请的内存空间记着匹配的去释放。ps 这个理想状态。但是如果碰上异常时就算注意释放了还是可能会出问题。需要下一条 智能指针 来管理才有保证。采用RAII思想或者智能指针来管理资源。 智能指针概念及使用
RAII
RAIIResource Acquisition Is Initialization是一种利用对象生命周期来控制程序资源如内存、文件句柄、网络连接、互斥量等等的简单技术。获取到一个资源后拿去初始化一个对象并且使用完将其释放。 在对象构造时获取资源接着控制对资源的访问使之在对象的生命周期内始终保持有效最后在对象析构的时候释放资源。借此我们实际上把管理一份资源的责任托管给了一个对象。 这种做法有两大好处
不需要显式地释放资源。采用这种方式对象所需的资源在其生命期内始终保持有效。
例如下面的例子
//使用RAII思想设计的Smart_Pointer类
//将开好的资源给Smart_Pointer这个类创建一个对象该对象里
//面是一个指针这个对象来管理这个资源当对象出了作用域
//自动调用析构函数将资源清理释放这样就不会有忘记释放资源
//或则上面中间异常导致delete不到的问题。
templateclass T
class Smart_Pointer{
public:Smart_Pointer(T* ptr):_ptr(ptr){}~Smart_Pointer(){cout ~Smart_Pointer endl;delete _ptr;}
private:T* _ptr;
};void func()
{Smart_Pointerint ptr(new int); throw(异常);
}
int main(){try {func();}catch (const char* s){cout s endl;}catch (...){cout 未知异常 endl;}return 0;
} 智能指针的原理
上述的SmartPointer 还不能将其称为智能指针因为它还不具有指针的行为。指针可以解引用也可以通过-去访问所指空间中的内容因此SmartPointer模板类中还得需要重载 * 、-才可让其像指针一样去使用。
举个例子
templateclass T
class Smart_Pointer
{
public:Smart_Pointer(T* ptr):_ptr(ptr){}T operator*(){return *_ptr;}T* operator-(){return _ptr;}~Smart_Pointer(){cout ~Smart_Pointer endl;delete _ptr;}
private:T* _ptr;
};
struct Date{Date(int year, int month, int day):_year(year),_month(month),_day(day){}~Date(){_year _month _day 0;}int _year;int _month;int _day;
};
int main(){Smart_Pointerint ptr1(new int(1));cout *ptr1 endl;Smart_PointerDate ptr2(new Date(2024, 4, 10));cout ptr2-_year ptr2-_month ptr2-_day endl;return 0;
}
//运行结果
//1
//2024 4 10
//~Smart_Pointer
//~Smart_Pointer
但是在实际中我们可能将指针指向另一个空间或指针间的拷贝赋值等问题。 那么智能指针的拷贝是需要深拷贝还是浅拷贝呢 答案是浅拷贝因为智能指针的行为是模拟指针的行为。 之前学习的数据结构比如listvector等容器特点是利用这些资源存储管理数据资源是自己的拷贝时希望资源各自一份不是同时利用同一份资源。 而智能指针与迭代器类似资源不是自己的代管资源拷贝的时候希望直指向同一块资源 那么智能指针的拷贝赋值等问题怎么解决呢那我们就来看看C中智能指针的历史发展。 C库多种智能指针详解
智能指针发展历史 C 98 中产生了第一个智能指针auto_ptr.C boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.C TR1引入了shared_ptr等。不过注意的是TR1并不是标准版。C 11引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。 版本一std::auto_ptrC98
文档链接https://legacy.cplusplus.com/reference/memory/auto_ptr/?kwauto_ptr
1. std::auto_ptr 使用
//包头文件memory
#include memory
int main()
{auto_ptrint ptr1(new int(10));auto_ptrint ptr2(new int(30));ptr1 ptr2; cout*ptr1endl; //30
}2. std::auto_ptr 原理
//当我们打印ptr1的内容时运行报错
int main()
{auto_ptrint ptr1(new int(10));auto_ptrint ptr2(ptr1);cout *ptr2 endl; //10//cout *ptr1 endl; //会报错
}运行调试时发现 ptr2拷贝构造ptr1前 ptr2拷贝构造ptr1后 原理总结 根据调试结果可以分析出 std::auto_ptr是将被拷贝对象值拷贝给拷贝对象然后将被拷贝对象置空处理
3. std::auto_ptr 模拟实现
C98版本的库中就提供了auto_ptr的智能指针。 auto_ptr的实现原理管理权转移的思想下面简化模拟实现了一份。
templateclass T
class auto_ptr
{
public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(auto_ptrT p){_ptr p._ptr;p._ptr nullptr;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}~auto_ptr(){cout ~Smart_Pointer endl;delete _ptr;}
private:T* _ptr;
};版本二unique_ptr (C11)
文档链接https://legacy.cplusplus.com/reference/memory/unique_ptr/?kwunique_ptr unique_ptr 版本的智能指针解决拷贝问题简单粗暴直接禁止拷贝和赋值。
1. unique_ptr 的使用
//包头文件memory
#include memory
int main()
{unique_ptr int ptr1(new int(10));//unique_ptr int ptr2(ptr1); //编译报错cout *ptr2 endl;//cout *ptr1 endl;
}2. unique_ptr 的原理
原理将unipue_ptr里面的拷贝构造与赋值运算符直接禁掉
3. unique_ptr 的模拟实现
templateclass T
class unique_ptr
{
public:unique_ptr(T* ptr):_ptr(ptr){}unique_ptr(unique_ptrT p) delete;unique_ptrT operator(unique_ptrT p) delete;T operator*(){return *_ptr;}T* operator-(){return _ptr;}~unique_ptr(){cout ~unique_ptr() endl;delete _ptr;}
private:T* _ptr;
};版本三 std::shared_ptr(C11)
文档链接https://legacy.cplusplus.com/reference/memory/shared_ptr/?kwunique_ptr shared_ptr 允许拷贝。
shared_ptr使用
int main()
{shared_ptrint ptr1(new int(10));shared_ptrint ptr2(ptr1); //可拷贝构造shared_ptrint ptr3;ptr3 ptr1; //可赋值cout *ptr2 endl;cout *ptr1 endl;cout *ptr3 endl;
}shared_ptr原理
shared_ptr的原理是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
shared_ptr在其内部给每个资源都维护了着一份计数用来记录该份资源被几个对象共享。在对象被销毁时(也就是析构函数调用)就说明自己不使用该资源了对象的引用计数减一。如果引用计数是0就说明自己是最后一个使用该资源的对象必须释放该资源如果不是0就说明除了自己还有其他对象在使用该份资源不能释放该资源否则其他对象就成野指针了。
注意share_ptr不允许隐式类型转换
思考为什么不能用静态成员变量来计数 如图所示 解析 当使用静态成员变量来计数如图所示开始时ptr1指向的一个空间当用ptr1拷贝构造或则复制拷贝一个对象ptr2时只需要_count当他们中的一个对象析构时只需要_count–当_count等于1时说明只有一个对象管控着这个资源当这个对象析构的时候释放这块空间
用静态成员变量虽然可以解决这种场景但是当重新构造另一个对象的时候该对象引用计数应该为1但是这里却不为一。 所以用静态成员变量计数行不通。
解决方法 对象里面存储一个int*的指针构造的时候将这个空间的值初始化为1当拷贝构造或者析构的时候只需要–即可 如图
shared_ptr模拟实现
templateclass T
class shared_ptr
{
public:shared_ptr(T* ptrnullptr):_ptr(ptr),_count(new int(1)){}//拷贝构造shared_ptr(shared_ptrT p){assert(_ptr ! p._ptr);_ptr p._ptr;_count p._count;(*_count);}//ptr1ptr2shared_ptrT operator(shared_ptrT p){//判断自己赋值给自己或则间接自己给自己赋值if (_ptr ! p._ptr){if (--(*_count) 0){delete _ptr;delete _count;}_ptr p._ptr;_count p._count;(*_count);}return *this;}int getconut(){return *(_count);}T operator*(){return *_ptr;}T* operator-(){return _ptr;}~shared_ptr(){cout ~shared_ptr() endl;//当引用计数--后为0则释放if (--(*_count)0){delete _ptr;delete _count;}}
private:T* _ptr;int* _count;
};循环引用问题
node1和node2两个智能指针对象指向两个节点引用计数变成1我们不需要手动delete。node1的_next指向node2node2的_prev指向node1引用计数变成2。node1和node2析构引用计数减到1但是_next还指向下一个节点。但是_prev还指向上一个节点。也就是说_next析构了node2就释放了。也就是说_prev析构了node1就释放了。但是_next属于node的成员node1释放了_next才会析构而node1由_prev管理_prev属于node2成员所以这就叫循环引用谁也不会释放。 解决方案 在引用计数的场景下把节点中的_prev和_next改成weak_ptr就可以了 原理就是node1-_next node2;和node2-_prev node1; 时weak_ptr的_next和_prev不会增加node1和node2的引用计数。
weak_ptr的简单实现
代码实现
templateclass T
class weak_ptr
{
public:weak_ptr():_ptr(nullptr){}weak_ptr(shared_ptrT p){_ptr p.get();}shared_ptrT operator(shared_ptrT p){_ptr p.get();return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}~weak_ptr(){cout ~weak_ptr() endl;delete _ptr; }private:T* _ptr;};本章完~