佛山网站推广seo,网站备案查询工信部手机版,网站流量在哪设置,品牌视觉形象设计案例目录 
1. 智能指针的引入_内存泄漏 
1.1 内存泄漏 
1.2 如何避免内存泄漏 
2. RAII思想 
2.1 RAII解决异常安全问题 
2.2 智能指针原理 
3. auto_ptr 
3.1 auto_ptr模拟代码 
4. unique_ptr 
4.1 unique_ptr模拟代码 
5. shared_ptr 
5.1 shared_ptr模拟代码 
5.2 循环引用 
6.…目录 
1. 智能指针的引入_内存泄漏 
1.1 内存泄漏 
1.2 如何避免内存泄漏 
2. RAII思想 
2.1 RAII解决异常安全问题 
2.2 智能指针原理 
3. auto_ptr 
3.1 auto_ptr模拟代码 
4. unique_ptr 
4.1 unique_ptr模拟代码 
5. shared_ptr 
5.1 shared_ptr模拟代码 
5.2 循环引用 
6. weak_ptr 
6.1 weak_ptr模拟代码 
7. 定制删除器了解 
8. 完整代码 
9. 笔试面试题 
9.1 智能指针的发展历史 
9.2 笔试选择题 
9.3 选择题答案及解析 
本篇完。 1. 智能指针的引入_内存泄漏 
为什么需要智能指针上一篇 1.1 内存泄漏 
上面是异常安全导致的内存泄漏问题开空间没有释放也可能导致内存泄漏。 什么是内存泄漏 内存泄漏指因为疏忽或错误逻辑错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失而是应用程序分配某段内存后因为设计错误失去了对该段内存的控制指针丢了因而造成了内存的浪费。内存泄漏的危害长期运行的程序出现内存泄漏影响很大如操作系统、后台服务等等出现内存泄漏会导致响应越来越慢最终卡死。 void MemoryLeaks()
{int* p1  (int*)malloc(sizeof(int)); // 1.内存申请了忘记释放int* p2  new int;int* p3  new int[10]; // 2.异常安全问题Func(); // 这里如果Func函数抛异常n会导致 delete[] p3未执行p3没被释放.delete[] p3;
} 内存泄漏分类了解 C/C程序中一般我们关心两种方面的内存泄漏堆内存泄漏(Heap leak) 堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一 块内存用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分 内存没有被释放那么以后这部分空间将无法再被使用就会产生Heap Leak。系统资源泄漏 指程序使用系统分配的资源比方套接字、文件描述符、管道等没有使用对应的函数释放 掉导致系统资源的浪费严重可导致系统效能减少系统执行不稳定。 1.2 如何避免内存泄漏  1. 工程前期良好的设计规范养成良好的编码规范申请的内存空间记着匹配的去释放。ps这个理想状态。但是如果碰上异常时就算注意释放了还是可能会出问题。需要下一条智能指针来管理才有保证。2. 采用RAII思想或者智能指针来管理资源。 3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。 4. 出问题了使用内存泄漏工具检测。ps不过很多工具都不够靠谱或者收费昂贵。总结 :内存泄漏非常常见解决方案分为两种 1、事前预防型。如智能指针等。2、事后查错型。如泄漏检测工具。 2. RAII思想 RAII是英文Resource Acquisition Is Initialization(资源请求即初始化)的首字母是一种利用对象生命周期来控制程序资源的简单技术。这些资源可以是内存文件句柄网络连接互斥量等等。 在对象构造时获取资源接着控制对资源的访问使之在对象的生命周期内始终保持有效最后在对象析构的时候释放资源。借此我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处 ① 不需要显式地释放资源。 ② 采用这种方式对象所需的资源在其生命期内始终保持有效 2.1 RAII解决异常安全问题 
利用RAII思想设计delete资源的类 
#include iostream
using namespace std;
double Division(int a, int b)
{if (b  0){throw Divide by Zero Error;}else{return ((double)a / (double)b);}
}
// 利用RAII思想设计delete资源的类
templateclass T
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){cout  delete:   _ptr  endl;delete _ptr;}
protected:T* _ptr;
};void Func()
{//1、如果p1这里new 抛异常会如何//2、如果p2这里new 抛异常会如何//3、如果div调用这里又会抛异常会如何//int* p1  new int;//int* p2  new int;//cout  Division()  endl;//delete p1;//delete p2;//cout  释放资源  endl; SmartPtrint sp1(new int);SmartPtrint sp2(new int);cout  Division(3, 0)  endl;
}int main()
{try{Func();}catch (const char* errmsg){cout  errmsg  endl;}catch (...){cout  unknown exception  endl;}cout  return 0;  endl;return 0;
} 
运行 把 Division(3, 0) 改为 Division(3, 1) 2.2 智能指针原理 上述的SmartPtr还不能将其称为智能指针因为它还不具有指针的行为。指针可以解引用也可以通过-去访问所指空间中的内容因此AutoPtr模板类中还得需要将* 、-重载下才可让其像指针一样去使用。 // 1、利用RAII思想设计delete资源的类
// 2、重载operator*和opertaor-具有像指针一样的行为。
// 3、浅拷贝问题析构两次下面讲
templateclass T
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){cout  delete:   _ptr  endl;delete _ptr;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}
protected:T* _ptr;
}; 所谓RAII就是将资源的生命周期和对象的生命周期绑定。从构造函数开始到析构函数结束。智能指针就是使用了RAII技术并且利用对象生命周期结束时编译器会自动调用对象的析构函数来释放资源。智能指针的智能就在于资源会被自动释放不需要显式地释放资源。采用智能指针对象所需的资源在其生命周期内始终保持有效。 总结智能指针的原理  1、利用RAII思想设计delete资源的类  2、重载operator*和opertaor-具有像指针一样的行为。 3、拷贝问题不同的智能指针的解决方式不一样 3. auto_ptr 
C98就已经提供了这样的一个智能指针注意到上面写着deprecated不推荐使用了 让上面写的SmartPtr使用编译器自动生成的拷贝构造函数 
#include iostream
using namespace std;
// 1、利用RAII思想设计delete资源的类
// 2、重载operator*和opertaor-具有像指针一样的行为。
// 3、浅拷贝问题
templateclass T
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){cout  delete:   _ptr  endl;delete _ptr;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}
protected:T* _ptr;
};int main()
{SmartPtrint sp1(new int);SmartPtrint sp2(sp1);return 0;
} 上面代码在运行时报错。 智能指针ap2拷贝复制了ap1此时ap1和ap2都指向同一块动态内存空间。 当程序执行结束以后ap1对象和ap2对象都会销毁并且会执行各自的析构函数所以那份动态空间就会被释放两次所以报错了。怎么解决 显式定义一个拷贝构造函数不能让两个智能指针指向同一份动态内存空间。但是这样没有很好的解决问题auto_ptr就是这样设计的 //auto_ptr
#include iostream
using namespace std;
// 1、利用RAII思想设计delete资源的类
// 2、重载operator*和opertaor-具有像指针一样的行为。
// 3、浅拷贝问题
templateclass T
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){cout  delete:   _ptr  endl;delete _ptr;}SmartPtr(SmartPtrT ptr):_ptr(ptr._ptr){ptr._ptr  nullptr;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}
protected:T* _ptr;
};int main()
{SmartPtrint sp1(new int);SmartPtrint sp2(sp1);return 0;
} 增加一个名字叫A的类重复上面操作 
class A
{
public:~A(){cout  ~A()  endl;}
protected:int _a1  0;int _a2  0;
};int main()
{SmartPtrA sp1(new A);SmartPtrA sp2(sp1);return 0;
} 使用一下库里的auto_ptr试一下 
class A
{
public:~A(){cout  ~A()  endl;}
protected:int _a1  0;int _a2  0;
};int main()
{//SmartPtrA sp1(new A);//SmartPtrA sp2(sp1);auto_ptrA sp1(new A);auto_ptrA sp2(sp1);return 0;
} 和显式定义一个拷贝构造函数的效果一样。 
auto_ptr到这种情况就崩了 
class A
{
public:~A(){cout  ~A()  endl;}
//protected:int _a1  0;int _a2  0;
};int main()
{//SmartPtrA sp1(new A);//SmartPtrA sp2(sp1);auto_ptrA sp1(new A);auto_ptrA sp2(sp1);sp1-_a1;sp1-_a2;return 0;
} 3.1 auto_ptr模拟代码 
上面SmartPtr再加一个赋值重载改下名字就差不多是auto_ptr的模拟了再用命名空间封一下 
赋值重载细节还挺多的前面学的赋值重载都类似拷贝构造可以不看先写写这里直接放代码 
#include iostream
#include memory
using namespace std;
//1、RAII
//2、像指针一样
//3、解决拷贝问题不同的智能指针的解决方式不一样namespace rtx
{templateclass Tclass auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}~auto_ptr(){cout  ~auto_ptr - delete:   _ptr  endl;delete _ptr;}auto_ptr(auto_ptrT ptr):_ptr(ptr._ptr){ptr._ptr  nullptr;}auto_ptrT operator(auto_ptrT ap){if (this ! ap) // 防止自己赋值给自己{if (_ptr) // 防止释放空delete空也行{cout  operator - Delete:  _ptr  endl;delete _ptr;}_ptr  ap._ptr;ap._ptr  nullptr;}return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}protected:T* _ptr;};
}class A
{
public:~A(){cout  ~A()  endl;}
//protected:int _a1  0;int _a2  0;
};int main()
{//SmartPtrA sp1(new A);//SmartPtrA sp2(sp1);rtx::auto_ptrA sp1(new A);rtx::auto_ptrA sp2(sp1);rtx::auto_ptrA sp3  sp2;return 0;
} 可以把命名空间切换到std比较一下auto_ptr使用的是管理权转移的办法会导致被拷贝对象悬空是不负责的拷贝对于不清楚auto_ptr这个特点的人来说拷贝后再次使用ap1就会出问题。 
auto_ptr是C98一个失败的设计被挂在了耻辱柱上很多公司明确要求不能使用auto_ptr。 C98到C11期间人们被迫用C更新探索的库boost库里的一些智能指针到了C11 
终于更新了三个智能指针unique_prtshared_ptrwead_ptr相当于抄boost库的作业了。 
下面我们介绍以及模拟实现这几个智能指针当然还有很多接口在模拟代码里没有实现。 
4. unique_ptr 
在C11中更加靠谱的unique_ptr智能指针 unique_ptr直接禁止使用拷贝构造函数即使编译器也不能生成默认的拷贝构造函数因为使用了delete关键字。 
unique_ptr采用的策略就是既然拷贝有问题那么就直接禁止拷贝这确实解决了悬空等问题使得unique_ptr是一个独一无二的智能指针。 
写到这发现忘记创建新项目了这里创建一个Test.cpp和SmartPtr.hpp.h.cpp直接.h也行都可以把函数的实现在里面实现。声明和定义分离只是为了保护源码 4.1 unique_ptr模拟代码 
直接复制一份auto_ptr代码过来用delete关键字禁言拷贝构造和赋值重载就行了 templateclass Tclass unique_ptr{public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){cout  ~unique_ptr - delete:   _ptr  endl;delete _ptr;}unique_ptr(unique_ptrT ptr)  delete;unique_ptrT operator(unique_ptrT ap)  delete;T operator*(){return *_ptr;}T* operator-(){return _ptr;}protected:T* _ptr;}; 关于delete关键字在5.2从C语言到C_33(C11_上)initializer_list右值引用完美转发移动构造/赋值_GR_C的博客-CSDN博客 5. shared_ptr 
unique_ptr禁掉了拷贝但是如果就想拷贝智能指针呢这就要用到shared_ptr了 shared_ptr采用了引用计数的方法来解决拷贝问题 
引用计数直接在成员变量加一个int Count可以吗每一个对象都有一个自己的Count显然是不对的我们应该让拷贝和被拷贝对象管理同一个Count。那么使用静态成员变量可以吗这也不可以因为这样所有的对象都管理的是同一个Count了包括没有拷贝的对象 shared_ptr原理 通过引用计数的方式来实现多个shared_ptr对象之间共享资源。例如 老师晚上在下班之前都会通知让最后走的学生记得把门锁下。 ① shared_ptr内部给每个资源都维护了一份计数用来记录该份资源被几个对象共享。 ② 在对象被销毁时(也就是析构函数调用)说明自己不使用该资源了对象引用计数减一。 ③ 如果引用计数是0就说明自己是最后一个使用该资源的对象必须释放该资源。 ④ 如果不是0就说明除了自己还有其他对象在使用该份资源不能释放该资源否则其他对象就成野指针了。 shared_ptr增加了一个成员类似int* _pCount解决这个问题 这样构造拷贝构造和析构函数就是这样的 构造先给 _pCount指向1析构无论什么时候都减减如果减减0就释放资源拷贝构造就是把指针也给它然后指针指向的内容加加。 
OK请你到这写一个赋值重载出来手写或者敲都行坏笑.jpg这里直接放代码了 
5.1 shared_ptr模拟代码 templateclass Tclass shared_ptr{public:shared_ptr(T* ptr  nullptr): _ptr(ptr), _pCount(new int(1)){}void Release(){if (--(*_pCount)  0) // 防止产生内存泄漏,和析构一样,写成一个函数{delete _ptr;delete _pCount;}}~shared_ptr(){Release();}shared_ptr(const shared_ptrT sp): _ptr(sp._ptr), _pCount(sp._pCount){(*_pCount);}shared_ptrT operator(const shared_ptrT sp){//if (this ! sp)if (_ptr ! sp._ptr) // 防止自己给自己赋值,注意不能比较this,类似s1  s2; 再来一次s1  s2;{                    // 比较_pCount也行//if (--(*_pCount)  0) // 防止产生内存泄漏,和析构一样,写成一个函数//{//	delete _ptr;//	delete _pCount;//}Release();_ptr  sp._ptr;_pCount  sp._pCount;(*_pCount);}return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}protected:T* _ptr;int* _pCount;// 引用计数,有多线程安全问题,学了linux再讲,不能用静态成员}; 赋值重载需要注意细节的都在注释写了可以自己画一个图看看。 5.2 循环引用 
shared_ptr 完美了吗并不是它有一个死穴循环引用。 
创建一个链表节点在该节点的析构函数中打印提示信息 
struct Node
{~Node(){cout  ~Node  endl;}int _val;std::shared_ptrNode _next;std::shared_ptrNode _prev;
}; 
将n1和n2互相指向形成循环引用 
因为要给_next和_prev赋值所以Node里也要用智能指针 
int main()
{std::shared_ptrNode n1(new Node);std::shared_ptrNode n2(new Node);n1-_next  n2;n2-_prev  n1;return 0;
} 
执行该程序后节点析构函数中的打印信息并没有打印说明析构出了问题。 如果不形成循环引用就会打印提示信息 可以调用shared_ptr里的use_count接口打印引用计数值 n1和n2刚创建的时候它两的引用计数值都是1。当两个节点循环引用后它们的引用计数值都变成了2。 n2先析构右边的引用计数变为1n1再析构左边的引用计数变为1然后就没了。 
左边结点的_next什么时候释放- 取决于左边的结点什么时候delete。 
左边的结点什么时候delete- 取决于右边结点的_prev。 
右边结点的_prev什么时候释放- 取决于右边的结点什么时候delete。 
右边的结点什么时候delete- 取决于左边结点的_next。 
左边结点的_next什么时候释放 - 回到一开始的问题进入死循环。 在循环引用中节点得不到真正的释放就会造成内存泄漏。 
循环引用的根本原因在于next和prev也参与了资源的管理。 
这个漏洞shared_ptr本身也解决不了所以就增加了weak_ptr来解决这个问题。 
解决办法就是让节点中的_next和_prev仅指向对方 
而不参与资源管理也就是计数值不增加。 
这里为了配合上面和给下面模拟weak_ptr演示给我们的shared_ptr加两个接口函数 6. weak_ptr 
weak_ptr是为解决循环引用问题而产生的可以把weak_ptr当作shared_ptr的小跟班weak_ptr主要用shared_ptr来构造所以weak_ptr的拷贝构造以及赋值都不会让引用计数值加1仅仅是指向资源。 把链表类里的指针换成weak_ptr解决循环引用问题 6.1 weak_ptr模拟代码 
weak_ptr中只有一个成员变量_ptr用来指向动态内存空间在默认构造函数中仅仅指向动态内存空间。拷贝构造函数和赋值运算符重载函数中拷贝和赋值的对象都是shared_ptr指针。 weak_ptr就是用来解决循环引用问题的所以拷贝和赋值的智能指针必须是shared_ptr。 
weak_ptr和shared_ptr并不是同一个类所以获取shared_ptr中的_ptr时不能直接访问需要通过shared_ptr的接口get()来获取。 templateclass T // 辅助型智能指针配合解决shared_ptr循环引用问题class weak_ptr // 没有RAII,不管理资源{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptrT sp):_ptr(sp.get()){}weak_ptr(const weak_ptrT wp):_ptr(wp._ptr){}weak_ptrT operator(const shared_ptrT sp){_ptr  sp.get();return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}protected:T* _ptr;}; 
换下命名空间  效果一样。 7. 定制删除器了解 
前面我们自己实现的所有智能指针中在释放动态内存资源的时候都只用了delete也就是所有new出来的资源都是单个的 如果是new int[10]或者malloc(20)呢当需要释放的资源是其他类型的呢delete肯定就不能满足了 对于不同类型的资源需要定制删除器。 先来看库中是如何实现的这里仅拿shared_ptr为例unique_ptr也是一样的。 在构造智能指针的时候可以传入定制的删除器。 可以采用仿函数的方式lambda的方式以及函数指针的方式只要是可调用对象都可以。 此时的智能指针指向的是动态数组我们传入的定制删除器也是释放数组的 
#include SmartPtr.hpp
#include memoryclass A
{
public:~A(){cout  ~A()  endl;}//protected:int _a1  0;int _a2  0;
};struct Node
{~Node(){cout  ~Node  endl;}int _val;rtx::weak_ptrNode _next;rtx::weak_ptrNode _prev;
};templateclass T
struct DeleteArray
{void operator()(T* ptr){cout  delete[]  ptr  endl;delete[] ptr;}
};templateclass T
struct Free
{void operator()(T* ptr){cout  free  ptr  endl;free(ptr);}
};int main()
{// 仿函数对象std::shared_ptrNode n1(new Node);std::shared_ptrNode n2(new Node[5], DeleteArrayNode());std::shared_ptrint n3(new int[7], DeleteArrayint());std::shared_ptrint n4((int*)malloc(sizeof(12)), Freeint());// lambdastd::shared_ptrNode n5(new Node);std::shared_ptrNode n6(new Node[5], [](Node* ptr) {delete[] ptr; });std::shared_ptrint n7(new int[7], [](int* ptr) {delete[] ptr; });std::shared_ptrint n8((int*)malloc(sizeof(12)), [](int* ptr) {free(ptr); });//std::shared_ptrFILE n9(fopen(test.txt, w), [](FILE* ptr) {fclose(ptr); }); //不安全了,但这样也行std::unique_ptrNode, DeleteArrayNode up(new Node[5]); // unique_ptr只能这样传return 0;
} 下面我们类似库里unique_ptr的方法给我们的shared_ptr弄个类似的定制删除器 
给我们的shared_ptr配一个默认删除方式的仿函数执行的是delete ptr。 
在shared_ptr类模板的模板参数中增加一个定制删除器的模板参数缺省值默认删除方式。 
在释放资源的时候在Release()中调用定制的删除器仿函数对象。 templateclass Tstruct Delete{void operator()(T* ptr){cout  delete:  ptr  endl;delete ptr;}};templateclass T, class D  DeleteTclass shared_ptr{public:shared_ptr(T* ptr  nullptr): _ptr(ptr), _pCount(new int(1)){}void Release(){if (--(*_pCount)  0) // 防止产生内存泄漏,和析构一样,写成一个函数{//delete _ptr;delete _pCount;//D del;//del(_ptr);D()(_ptr); // 对_ptr直接用匿名对象删掉}}~shared_ptr(){Release();}
...........................略 下面放了这个程序运行的完整代码 
标准库中的shared_ptr在使用定制删除器的时候是在构造对象时传入函数对象来实现的。我们自己实现的shared_ptr是在实例化时传入仿函数类型实现的。 
这是因为C11标准库实现的方式和我们不一样它的更加复杂专门封装了几个类管理引用计数以及定制删除器等内容。 8. 完整代码 
SmartPtr.hpp 
#include iostream
using namespace std;
//1、RAII
//2、像指针一样
//3、解决拷贝问题不同的智能指针的解决方式不一样namespace rtx
{templateclass Tclass auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}~auto_ptr(){cout  ~auto_ptr - delete:   _ptr  endl;delete _ptr;}auto_ptr(auto_ptrT ptr):_ptr(ptr._ptr){ptr._ptr  nullptr;}auto_ptrT operator(auto_ptrT ap){if (this ! ap) // 防止自己赋值给自己{if (_ptr) // 防止释放空delete空也行{cout  operator - Delete:  _ptr  endl;delete _ptr;}_ptr  ap._ptr;ap._ptr  nullptr;}return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}protected:T* _ptr;};templateclass Tclass unique_ptr{public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){cout  ~unique_ptr - delete:   _ptr  endl;delete _ptr;}unique_ptr(unique_ptrT ptr)  delete;unique_ptrT operator(unique_ptrT ap)  delete;T operator*(){return *_ptr;}T* operator-(){return _ptr;}protected:T* _ptr;};templateclass Tstruct Delete{void operator()(T* ptr){cout  delete:  ptr  endl;delete ptr;}};templateclass T, class D  DeleteTclass shared_ptr{public:shared_ptr(T* ptr  nullptr): _ptr(ptr), _pCount(new int(1)){}void Release(){if (--(*_pCount)  0) // 防止产生内存泄漏,和析构一样,写成一个函数{//delete _ptr;delete _pCount;//D del;//del(_ptr);D()(_ptr); // 对_ptr直接用匿名对象删掉}}~shared_ptr(){Release();}shared_ptr(const shared_ptrT sp): _ptr(sp._ptr), _pCount(sp._pCount){(*_pCount);}shared_ptrT operator(const shared_ptrT sp){//if (this ! sp)if (_ptr ! sp._ptr) // 防止自己给自己赋值,注意不能比较this,类似s1  s2; 再来一次s1  s2;{                    // 比较_pCount也行//if (--(*_pCount)  0) // 防止产生内存泄漏,和析构一样,写成一个函数//{//	delete _ptr;//	delete _pCount;//}Release();_ptr  sp._ptr;_pCount  sp._pCount;(*_pCount);}return *this;}int use_count(){return *_pCount;}T* get() const{return _ptr;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}protected:T* _ptr;int* _pCount;// 引用计数,有多线程安全问题,学了linux再讲,不能用静态成员};templateclass T // 辅助型智能指针配合解决shared_ptr循环引用问题class weak_ptr // 没有RAII,不管理资源{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptrT sp):_ptr(sp.get()){}weak_ptr(const weak_ptrT wp):_ptr(wp._ptr){}weak_ptrT operator(const shared_ptrT sp){_ptr  sp.get();return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}protected:T* _ptr;};
} 
Test.cpp 
#include SmartPtr.hpp
#include memoryclass A
{
public:~A(){cout  ~A()  endl;}//protected:int _a1  0;int _a2  0;
};struct Node
{~Node(){cout  ~Node  endl;}int _val;rtx::weak_ptrNode _next;rtx::weak_ptrNode _prev;
};templateclass T
struct DeleteArray
{void operator()(T* ptr){cout  delete[]  ptr  endl;delete[] ptr;}
};templateclass T
struct Free
{void operator()(T* ptr){cout  free  ptr  endl;free(ptr);}
};int main()
{仿函数对象//std::shared_ptrNode n1(new Node);//std::shared_ptrNode n2(new Node[5], DeleteArrayNode());//std::shared_ptrint n3(new int[7], DeleteArrayint());//std::shared_ptrint n4((int*)malloc(sizeof(12)), Freeint());lambda//std::shared_ptrNode n5(new Node);//std::shared_ptrNode n6(new Node[5], [](Node* ptr) {delete[] ptr; });//std::shared_ptrint n7(new int[7], [](int* ptr) {delete[] ptr; });//std::shared_ptrint n8((int*)malloc(sizeof(12)), [](int* ptr) {free(ptr); });std::shared_ptrFILE n9(fopen(test.txt, w), [](FILE* ptr) {fclose(ptr); }); //不安全了,但这样也行//std::unique_ptrNode, DeleteArrayNode up(new Node[5]); // unique_ptr只能类似这样传rtx::shared_ptrNode n1(new Node);rtx::shared_ptrNode, DeleteArrayNode n2(new Node[5]);rtx::shared_ptrint, DeleteArrayint n3(new int[5]);rtx::shared_ptrint, Freeint n4((int*)malloc(sizeof(12)));return 0;
} 9. 笔试面试题 
面试很大几率会让手撕一个指针指针如果没有要求的话可以写一个uniqeu_ptr别写auto_ptr就行有要求的话就应该就是shared_ptr了所以智能指针的模拟实现应该闭着眼都能手撕出来。 
笔试面试常问问题 上面的问题博客上面都讲了总结下智能指针的发展历史 
9.1 智能指针的发展历史 C98中的auto_ptr存在非常大的缺陷在拷贝构造或者赋值的时候原本的auto_ptr会被置空所以这个智能指针存在非常大的缺陷很多地方都禁止使用。 C11中的unique_ptr禁止了拷贝和赋值直接避免了auto_ptr可能存在的缺陷是一个独一无二的智能指针但是它不能拷贝和赋值。 C11又提供了shared_ptr通过引用计数的方式解决了不能拷贝和赋值的缺陷并且通过互斥锁保证了shared_ptr本身的线程安全但是它的死穴是循环引用。 C11为了解决shared_ptr的循环引用问题又提供了weak_ptr智能指针通过仅指向不管理的方式解决了这个问题。 在使用的时候要根据具体情况选择合适的智能指针切记最好不要使用auto_ptr。 C委员会还发起了一个库叫boost库这个库可以理解为C标准库的先行版boost库中好用的东西会被C标准库收录标准库中的智能指针就是参照boost库中的智能指针再加以修改定义出来的。  C11和boost中智能指针的关系 ① 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中的实现的。 9.2 笔试选择题 
1. 以下那个误操作不属于资源泄漏 
A.打开的文件忘记关闭 
B.malloc申请的空间未通过free释放 
C.栈上的对象没有通过delete销毁 
D.内存泄漏属于资源泄漏的一种但资源泄漏不仅仅是内存泄漏 2. 下面那个说法可以表示资源泄漏 
A.从商店买东西 
B.借钱不还 
C.买房子交首付 
D.办信用卡 3. 下面关于内存泄漏的说法正确的是 
A.如果对程序影响不是很大的情况下泄漏一两个字节不是很重要 
B.内存没有释放时进程在销毁的时候会统一回收不用担心 
C.内存泄漏不一定会对系统马上造成影响可以不着急进行处理 
D.写代码时要有良好的编码规范万一发生内存泄漏要及时处理 4. 关于RAII下面说法错误的是 
A.RAII的实现方式就是在构造函数中将资源初始化在析构函数中将资源清理掉 
B.RAII方式管理资源可以有效避免资源泄漏问题 
C.所有智能指针都借助RAII的思想管理资源 
D.RAII方式管理锁有些场景下可以有效避免死锁问题 5. 下面关于auto_ptr的说法错误的是 
A.auto_ptr智能指针是在C98版本中已经存在的 
B.auto_ptr的多个对象之间不能共享资源 
C.auto_ptr的实现原理是资源的转移 
D.auto_ptr完全可以正常使用 6. 下面关于unique_ptr说法错误的是 
A.unique_ptr是C11才正式提出的 
B.unique_ptr可以管理一段连续空间 
C.unique_ptr不能使用其拷贝构造函数 
D.unique_ptr的对象之间不能相互赋值 
7. 下面关于shared_ptr说法错误的是 ( ) 
A.shared_ptr是C11才正式提出来的 
B.shared_ptr对象之间可以共享资源 
C.shared_ptr可以应用于任何场景 
D.shared_ptr是借助引用计数的方式实现的 8. 下面关于weak_ptr的说法错误的是 
A.weak_ptr与shread_ptr的实现方式类似都是通过引用计数的方式实现的 
B.weak_ptr的对象可以独立管理资源 
C.weak_ptr的唯一作用就是解决shared_ptr中存在的循环引用问题 
D.weak_ptr一般情况下都用不到 9.3 选择题答案及解析 1. C A属于打开的文件用完时一定要关闭 B属于堆上申请的空间需要用户显式的释放 C不属于栈上的对象不需要释放函数结束时编译器会自动释放 D正确资源泄漏包含的比较广泛比如文件未关闭、套接字为关闭等 2. B                 从系统中动态申请的资源一定要记着及时归还否则别人可能就使用不了或者申请失败。就像借钱一样 3. D A错误一两个字节不处理时可能会因小失大很多个两字节就是很大的一块内存空间 B错误虽然进程退出时会回收但是进程为退出时可能会影响程序性能甚至会导致崩溃 C错误只要发现了就要及时处理否则说不定什么时候程序就会崩溃 D正确内存泄漏最好不要发生万一发生了一定要及时处理 4. C C错误weak_ptr不能单独管理资源必须配合shared_ptr一块使用解决shared_ptr中存在的 循环引用问题 5. D A正确 B正确因为auto_ptr采用资源管理权转移的方式实现的比如用ap1拷贝构造ap2时ap1中 的资源会转移给     ap2而ap1与资源断开联系 C正确 D错误可以使用但是不建议用因为有缺陷标准委员会建议什么情况下都不要使用auto_ptr 6. B A正确 B错误C11中提供的智能指针都只能管理单个对象的资源没有提供管理一段空间资源的智能指针 C正确因为unique_ptr中已经将拷贝构造函数和赋值运算符重载delete了 D正确原因同C 7. C C错误有些场景下shared_ptr可能会造成循环引用必须与weak_ptr配合使用 8. B A正确weak_ptr和shared_ptr都是通过引用计数实现但是在底层还是有区别的 B错误weak_ptr不能单独管理资源因为其给出的最主要的原因是配合shared_ptr解决其循环引用问题 C正确处理解决shared_ptr的循环引用问题外别无它用 D正确 本篇完。 
shared_ptr还有线程安全问题没办法讲需要学完Linux多线程后再讲。C一些关于Linux的内容更新到Linux多线程的内容后后再放出来应该一两篇就够了。 
继承和多态是C第一重要的话智能指针应该算是C第二重要的部分了后几篇算是C收尾了。下一篇特殊类设计和C的类型转化。