• C++11 - 8 -智能指针


    c++

    前言:

    Vue框架:从项目学Vue
    OJ算法系列:神机百炼 - 算法详解
    Linux操作系统:风后奇门 - linux

    普通指针:

    安全隐患:

    其他函数异常:

    • 查看下面这段存在异常的函数:
    int div(){
    	int a, b;
    	cin >>a >>b;
    	if(b == 0)
    		throw invalid_argument("除0错误");
    	return a/b;
    }
    
    void func(){
    	int *p = new int;
    	cout<<div() <<endl;
    	delete p;
    }
    int main(){
    	try{
    		func();
    	}catch(const exception &e){
    		cout<< e.what() <<endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 异常来源:
      如果div()函数异常,由于不设置try catch机制,申请的p指针无法被delete释放
    • 处理策略:try catch
    int div(){
    	int a, b;
    	cin >>a >>b;
    	if(b == 0)
    		throw invalid_argument("除0错误");
    	return a/b;
    }
    
    void func(){
    	int *p = new int;
    	try{
    		cout<<div() <<endl;
    	}catch(const exception &e){
    		cout<< e.what() <<endl;
    	}
    	delete p;
    }
    int main(){
    	try{
    		func();
    	}
    	catch(const exception &e){
    		cout<< e.what() <<endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    new函数异常:

    • 多次为指针申请空间,之后统一释放。

      当一次申请出错时,前面所有申请的资源无法释放:

    int main(){
    	int *p1 = new int;
    	int *p2 = new int;
    	int *p3 = new int;
    	delete p1;
    	delete p2;
    	delete p3;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 处理策略:
      1. try + catch
      2. 设置初始值为nullptr,申请失败后值还为nullptr
    int main(){
    	int *p1 = nullptr;
    	int *p2 = nullptr;
    	int *p3 = nullptr;
    	try{
    		int *p1 = new int;
    		int *p2 = new int;
    		int *p3 = new int;
    	}catch(...){
    		if(p2 == nullptr)
    			delete p1;
    		if(p3 == nullptr){
    			delete p1;
    			delete p2;
    		}
    	}
    	delete p1;
    	delete p2;
    	delete p3;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    智能指针:

    RAII原理:

    • RAII:resource acquisition is initialization

      1. 利用对象生命周期控制程序资源
      2. 在对象构造时获取资源
      3. 在对象析构时释放资源
      4. 实际上将一份资源的管理职责交给了一个对象
    • 好处:

      1. 不需要显式释放资源
      2. 对象所需资源在对象的生命周期内始终有效
    • 实质:

      一个成员变量是模板指针的类

      模板指针指向new函数的返回指针

      资源无效后析构时释放模板指针所指资源

    smart_ptr:

    • 析构函数存在bug的版本:
    template <class T>
    class smart_ptr{
    	private:
    		T *ptr;
    	public:
    		smart_ptr(T *_ptr){
    			ptr = _ptr;
    		}
    		T& operator*(){
    			return *ptr;
    		}
    		T* operator->(){
    			return ptr;
    		}
    		//有bug的析构函数
    		~smart_ptr(){
    			cout<<ptr <<" " <<*ptr <<endl;
    			delete ptr;
    		}
    }
    int main(){
    	smart_ptr<int> sp1(new int);
    	smart_ptr<int> sp2(new int);
    	smart_ptr<int> sp3(new int);
    	
    //析构函数bug体现:同一空间被析构两次,肯定报错
    	int *p = new int;
    	smart_ptr<int> sp4 = p;
    	smart_ptr<int> sp5 = p;
    	cout<<div() <<endl;
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 很明显,这一版的SmartPtr不是存在缺点,而是存在析构漏洞,根本不能使用
    • 下面介绍C++98和C++11中几种防止SmartPtr()多次析构的不同设计

    auto_ptr:

    管理权转移:

    • 出现年代:c++98
    • 解决多次delete同一资源的方案:管理权转移
      每指向资源的指针多一个时
      1. 将对资源的管理权交给最新的指针
      2. 同时原来的指针都指向空
    • 代码:
    template <class T>
    class auto_ptr{
    	private:
    		T *ptr;
    	public:
    		auto_ptr(T *_ptr){
    			ptr = _ptr;
    		}
    		auto_ptr(auto_ptr<T> &ap){
    			ptr = ap.ptr;
    			ap.ptr = nullptr;
    		}
    		auto_ptr<T>& operator=(auto_ptr<T> *ap){
    			if(this != &ap){
    				if(ptr)
    					delete ptr;
    				ptr = ap.ptr;
    				ap.ptr = nullptr;
    			}
    			return *this;
    		}
    		T& operator*(){
    			return *ptr;
    		}
    		T* operator->(){
    			return ptr;
    		}
    		~auto_ptr(){
    			delete ptr;
    		}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    优点:

    • 优点:

      直接了当的解决了双重析构的问题

    缺点:

    • 缺点1:

      新的auto_ptr到来会覆盖旧的auto_ptr

      对于auto_ptr使用不熟悉的同学可能出这样的错

    int main(){
    	auto_ptr<int> sp1(new int);
    	auto_ptr<int> sp2(sp1);
    	*sp2 = 10;
    	cout<< *sp2 <<endl;
    	cout<< *sp1 <<endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 缺点2:

      管理权转移只存在于auto_ptr对象之间拷贝

      当存在两个auto_ptr对象都从一个指针初始化而来时,还是会出现双重析构问题:

    int main(){
    	int *p = new int(10);
    	auto_ptr<int> ap1(p);
    	auto_ptr<int> ap2(p);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    unique_str:

    • 出现年代:boost库,同期三个智能指针为scoped_ptr / shared_ptr / weak_ptr
    • C++11对boost库的借鉴产物:unique_ptr / shared_ptr / weak_ptr
    • 解决多次delete同一资源的方案:防拷贝
      每个指针开辟空间之后,只能有一个unique_ptr/scoped_ptr对象封装该指针

    防拷贝:

    • 防止拷贝函数:
      1. 法一:delete关键词
      2. 法二:私有 + 空实现(只声明不实现)
    • 代码:
    template <class T>
    class unique_ptr{
    	private:
    		T *ptr;
    		//c++11中delete关键字屏蔽函数
    		unique_ptr(unique_ptr<T> const &) = delete;
    		unique_ptr& operator=(unique_ptr<T> const &) = delete;
    
    		//c++98中私有 + 只声明不实现
    		unique_ptr(unique_ptr<T> const &);
     		unique_ptr& operator=(unique_ptr<T> const &);
    	public:
    		unique_ptr(T *_ptr){
    			ptr = _ptr;
    			
    		}
    		~unique_ptr(){
    			delete ptr;
    		}
    		void Show(){
    			cout<<*ptr <<endl;
    		}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    缺点:

    • 理论上每个指针都只能存在一个unique_ptr对象

    • 当不采用unique_ptr对象拷贝赋值,

      而是直接使用指针初始化两个unique_ptr对象时,还是存在双重析构

    int *p = new int(10);
    unique_ptr<int>  uq1(p);
    unique_ptr<int>  uq2(p);
    
    • 1
    • 2
    • 3

    shared_ptr:

    • 原理:
      1. 记录有多少个对象管理着同一块资源
      2. 每个对象析构时计数器 - -
      3. 每个对象构造时计数器 ++
      4. 最后一个析构的对象负责释放资源

    静态引用计数器:

    代码:
    • 代码:
    template <class T>
    class shared_ptr{
    	private:
    		static int refCount;
    		T *ptr;
    	public:
    		shared_ptr(T *_ptr){
    			refCount = 0;
    			ptr = _ptr;
    		}
    		shared_ptr(auto_ptr &ap){
    			refCount++;
    			ptr = ap.ptr;
    		}
    		~shared_ptr(){
    			refCount--;
    			if(refCount == 0 && ptr){
    				delete ptr;
    			}
    		}
    };
    int main(){
    	shared_ptr<int> sp1(new int);
    	shared_ptr<int> sp2(sp1);
    	shared_ptr<int> sp3(sp1);
    	
    	shared_ptr<int> sp4(new int);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    漏洞:
    计数器混乱问题:
    • 应该是每个资源独立使用一个自己的计数器

      如果所有资源都使用同一个引用计数器,那么会产生如下结果

    • 初始化时:

      1. sp1 借助 int* 初始化时,计数器refCount == 0
      2. sp2 借助 sp1 初始化时,计数器refCount == 1
      3. sp3 借助 sp1 初始化时,计数器refCount == 2
      4. sp4 借助 int* 初始化时,计数器refCount == 0
    • 析构时:

      sp1 & sp2 & sp3都对同一块内存析构,引发多重析构异常

    直接指针构造问题:
    • 继续不按套路出牌,直接使用指针构造两个对象:
    int *p = new int(10);
    shared_ptr<int> sp1(p);		//静态计数器refCount == 0
    shared_ptr<int> sp2(p);		//静态计数器refCount == 0
    /*
    	析构时直接双重析构,异常
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 静态引用计数器本身存储漏洞,且不能解决指针直接构造对象问题,

      下面来看看动态引用计数器能不能同时解决两个问题?

    动态引用计数器:

    代码:
    • 代码:
    template <class T>
    class shared_ptr{
    	private:
    		T *ptr;
    		int *refCount;			//动态引用计数器
    	public:
    		shared_ptr(T *_ptr){
    			ptr = _ptr;
    			refCount = (new int(1));	
    			//从这步开始已经决定了智能指针只能走对象拷贝路线,不能走指针直接构造路线
    		}
    		shared_ptr(const shared_ptr<T> &sp){
    			ptr = sp.ptr;
    			refCount = sp.refCount;
    			(*refCount)++;
    		}
    		shared_ptr<T>& operator=(const shared_ptr<T> &sp){
    			if(ptr != sp.ptr){
    				ptr = sp.ptr;
    				refCount = sp.refCount;
    				(*refCount)++;
    			}
    		}
    		~shared_ptr(){
    			if(--(*refCount) == 0 && ptr){
    				delete ptr;
    				delete refCount;			
    			}
    		}
    };
    int main(){
    	shared_ptr<int> sp1(new int);
    	shared_ptr<int> sp2(sp1);
    	shared_ptr<int> sp3(sp1);
    	
    	shared_ptr<int> sp4(new int);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    优点:
    • 每个资源独立使用自己的计数器

      不同的资源计数器之间互不干扰

    缺点:
    对象赋值拷贝时的计数器错误:
    • 两个指针所指不同的对象,又发生赋值拷贝时:
    class shared_ptr{
    	shared_ptr<T>& operator=(const shared_ptr<T> &sp){
    			if(ptr != sp.ptr){
    				ptr = sp.ptr;
    				refCount = sp.refCount;
    				(*refCount)++;
    			}
    		}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 发生资源引用数只增不减,可能最终导致无法析构释放:
      引用数错误
    • 对策:每次对象拷贝构造时,先让原资源引用数–,再让现资源引用数++
    class shared_ptr{
    	shared_ptr<T>& operator=(const shared_ptr<T> &sp){
    			if(ptr != sp.ptr){
    				//原资源引用数--
    				if(--(*refCount) == 0){
    					delete ptr;
    					delete refCount;
    				}
    				
    				//先资源引用数++
    				ptr = sp.ptr;
    				refCount = sp.refCount;
    				(*refCount)++;
    			}
    		}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    多线程下的计数器安全问题:
    • 当多线程同时含有对同一块资源的智能指针时,可能出现下列情况:
      1. 计数器++时少加:
        1. 主线程中智能指针被创建出来,refCount = 1;
        2. 子线程1中对主线程智能指针对象拷贝,refCount还未++完成(++分三个原子步骤)
        3. 子线程2中对主线程智能指针对象拷贝,refCount++完成
        4. 子线程1中refCount++完成
        5. 此时对同一资源的引用计数器本来应该是3,但是只有2
      2. 计数器- -时少减:
        1. 所有线程中只有两个智能指针对象时:
        2. 子线程1中使用完毕智能指针对象,开始析构,但是refCount–未完成(–分三个原子步骤)
        3. 子线程2中也使用完毕智能指针对象,开始析构,refCount- -完成
        4. 子线程2本来refCount- -之后,refCount==0,开始delete。但是此时refCount不为0
        5. 子线程1完成refCount–,资源无人delete,造成内存泄露
    • 线程安全对策:
      1. 加锁
      2. 锁也要借鉴refCount的原理,使用指针完成“一人一把锁”

    真·智能指针:

    代码:
    • 吸收动态引用计数的两大缺点之后,

      我们终于可以写出基本没有安全问题的智能指针类了:

    #include 
    #include 
    using namespace std;
    template <class T>
    class SharedPtr{
    	private:
    		T *ptr;
    		int *refCount;
    		mutex *mtx;
    	private:
    		void AddRefCount(){
    			mtx.lock();
    			*refCount++;
    			mtx.unlock();
    		}
    		void SubRefCount(){
    			bool flag = 0;
    	 		mtx.lock();
     			if (--(*refCount) == 0){
    				delete ptr;
    				delete refCount;
     				flag = true;
     			}
     			mtx.unlock();
     			if(flag == 1)
     				delete mtx;
    		}
    	public:
    		SharedPtr(T *_ptr){
    			ptr = _ptr;
    			refCount = new int(0);
    			mtx = new mutex;
    		}
    		
    		//默认采用拷贝构造的对象暂无ptr/refCount/mtx 
    		SharedPtr(SharedPtr<T> &sp){
    			ptr = sp.ptr;
    			refCount = sp.refCount;
    			mtx = sp.mtx;
    			AddRefCount();
    		}
    		
    		//默认采用赋值构造的对象已有ptr/refCount/mtx 
    		SharedPtr<T>& operator=(SharedPtr<T> &sp){
    			if(ptr != sp.ptr){
    				SubRefCount();
    				ptr = sp.ptr;
    				refCount = sp.refCount;
    				mtx = sp.mtx;
    				AddRefCount();
    			}
    		}
    		~ShardPtr(){
    			SubRefCount();
    		}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    优点:
    1. 类的基本优点:指针所指资源不用时自动析构释放

    2. 多个智能指针对象共享一块资源:

      每一块资源都独立使用一个引用计数器

      直接指针构造 / 拷贝构造 / 赋值构造 / 析构,都不会出现多重析构

    3. 线程安全:不会出现引用计数器少++ / 少- -的情况

    缺点:
    1. 多个智能指针对象直接使用指针构造对象时,还是存在多重析构异常:
    int *p = new int(10);
    shared_ptr<int> sp1(p);
    shared_ptr<int> sp2(p);
    shared_ptr<int> sp3(p);
    
    • 1
    • 2
    • 3
    • 4
    1. 关于拷贝构造是否要先减少原有资源引用计数器,需要单独为使用者说明:
      1. 如果拷贝构造只能使用在空白对象上,则不需要减少原有资源引用计数器
      2. 如果拷贝构造只能使用已经赋值过的对象上,则需要减少原有资源引用计数器
    漏洞:循环引用
    • 查看下面的链表节点智能指针,分析析构过程:
    struct ListNode{
    	int data;
    	shared_ptr<ListNode> prev;
    	shared_ptr<LIstNode> next;
    	~ListNode(){
    		cout<<"~ListNode()"<<endl;
    	}
    }
    
    int main(){
    	shared_ptr<ListNode> node1(new ListNode);
    	shared_ptr<ListNode> node2(new ListNode);
    	node1->next = node2;
    	node2->next = node1;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 析构一个节点需要析构三部分

      1. 值date
      2. 前驱指针
      3. 后继指针
    • 当前两块资源的引用计数器状态:
      循环引用

    • 要释放node1,需要资源引用计数器==0

      node1完成refCount–后,refCount==1

      要想refCount继续–,需要释放node2的next

      要释放node2的next,需要释放node2

    • 要释放node2,需要资源引用计数器==0

      node2完成refCount–后,refCount==1

      要想refCount继续–,需要释放node1的prev

      要想释放node1的prev,需要释放node1

    • 发现出现了类似锁套锁的死锁情况,不过这里叫循环引用

      下面来看c++11中解决循环引用的weak_ptr<>

    weak_ptr:

    原理:

    • 循环引用出现的根本原因:
    	node1->next = node2;
    	node2->next = node1;
    	//各自资源引用计数器数目 +1 了
    
    • 1
    • 2
    • 3
    • weak_ptr避免循环引用的对策:
      1. 增加引用对象时,不会增加资源计数器
      2. 析构引用对象时,也不会发生双重析构

    使用:

    • 我们就不自己手动模拟实现了:#include < memory>
    struct ListNode{
    	int _data;
    	weak_ptr<ListNode> _prev;
    	weak_ptr<ListNode> _next;
    	~ListNode(){
    		cout << "~ListNode()" << endl;
    	}
    };
    int main()
    {
     shared_ptr<ListNode> node1(new ListNode);
     shared_ptr<ListNode> node2(new ListNode);
     node1->_next = node2;
     node2->_prev = node1;
     return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    定制删除器:

    背景:

    • 不论是功能完整的shared_ptr还是特殊场景下的weak_ptr,

      既然称为智能指针,就可以接收很多类型的指针

      但是不同类型指针所指资源释放方式不同:

      1. new开辟的空间的指针 -> delete删除
      2. new[]开辟的空间的指针 -> delete[]删除
      3. malloc开辟的空间的指针 -> free释放
      4. fopen()打开的文件的指针 -> fclose()关闭
    • 所以我们在单纯为这些智能STL类传入指针的同时,也应该传入删除指针的方式,这就叫做定制删除器

    删除器类型:

    • 删除功能本质还是一个可调用对象:

      1. 函数名 / 函数指针
      2. 仿函数类对象
      3. lambda表达式
    • 重提一下new delete 和 new[] delete[]的区别:

      1. new出来的空间全部都是存储内容,delete按照类型大小+内存开头地址删除即可
      2. new[]出来的空间开头存储的是元素个数,delete按照元素个数+类型大小+内存开头地址进行删除
      3. 本质上两组关键字看待内存空间的方式不一致,即使是一个bit的区别,造成的差异也很大
      4. 但是现在很多编译器做了优化,new[]出来的含有个数的空间,也可以被delete删除释放

    模拟实现:

    • 由于类内要保存传入的删除器函数及其类型,所以只能采用模板仿函数类,而不能采用函数参数:
    template <class T>
    class default_delete{
    	public:
    		void operator()(const T*ptr){
    			cout<<"delete:"<<ptr<<endl;
    			delete ptr;
    		}
    };
    template <class T, class D = default_delete<T>>
    class del_ptr{
    	private:
    		T *ptr;
    	public:
    		unique_ptr(T *_ptr){
    			ptr = _ptr;
    		}
    		~unique_ptr(){
    			if(ptr){
    				D del;
    				del(ptr);
    			}
    		}
    };
    struct DeleteArray{
    	void operator()(A* ptr){
    		cout<< "delete[] : "<<ptr <<endl;
    		delete[] ptr;
    	}
    };
    struct DeleteFile{
    	void operator()(FILE* ptr){
    		cout<< "fclose[] : "<<ptr <<endl;
    		fclose(ptr);
    	}
    };
    int main(){
    	del_ptr<A> sp1(new A);	//默认删除器
    	del_ptr<A, DeleteArray> sp2(new A[10]);
    	del_ptr<A, DeleteFile> sp3(fopen("test.txt", "r"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    STL中的删除器:

    • STL实现较为复杂,同时支持:
      1. 函数参数传入删除器函数
      2. 模板类内保存删除器模板仿函数类
    模板传入仿函数类:
    • 模板仿函数类:
    struct DeleteArray{
    	void operator()(A* ptr){
    		cout<< "delete[] : "<<ptr <<endl;
    		delete[] ptr;
    	}
    };
    int main(){
    	unique_ptr<A> sp1(new A);
    	unique_ptr<A, DeleteArray> sp1(new A);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    传参传入删除函数:
    • 传参传入删除函数:
    int main(){
    	unique_ptr<A> sp1(new A[10], [](A* p){
    		delete[] p;
    	},);
    	unique_ptr<A> sp2(fopen("test.txt","r"), [](FILE *p){
    		fclose(p);
    	});
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    新需求:实现一个自动运维部署工具
    vue3黑马笔记
    mysql中自定义变量(浅显易懂简洁版)
    【数据开发】大数据平台架构,Hive / THive介绍
    SQL注入攻击分为几类?如何防御?
    计算机网络笔记【面试】
    基于Springboot+Vue的社区医院管理系统
    百度最强中文AI作画大模型
    ArcGIS:如何通过欧氏距离分析、重分类等工具对学校选址问题进行分析
    用C语言解决三个整数比大小,x,y,z三个整数求最小整数,从键盘上输入3个不同的整数×,y,Z,请设计一个算法找出其中最小的数,并画出流程图。
  • 原文地址:https://blog.csdn.net/buptsd/article/details/126879444