c++11——智能指针(代码片段)

两片空白 两片空白     2023-01-24     615

关键词:

目录

前言

一.智能指针的原理

        1.1 RAII思想

        1.2 原理

二.智能指针的分类

        2.1 auto_ptr介绍

        2.2 unique_pt介绍

         2.3 shared_ptr介绍


前言

        由于C++没有GC(垃圾回收器),程序员从堆上申请的资源,打开的文件,创建的套接字需要我们手动释放和关闭。

        但是可能会出现两个问题:

  1. 异常安全问题。申请完资源,由于异常需要捕捉,使得执行流不会按顺序进行,导致资源还没有释放和关闭,就跳到别的地方执行了。
  2. 由于需要程序员手动释放和关闭,可能会有忘记释放和关闭的问题。

        这样就会导致资源的泄漏。计算机的资源是有限的,导致我们可以使用的资源越来越少。

        智能指针是针对我们从堆上申请的空间,比如:new和malloc出来的空间。使得智能指针来帮我们释放空间,不需要我们来手动释放了。

一.智能指针的原理

        1.1 RAII思想

        RAII是一种了利用对象生命周期来控制程序资源(如:内存,文件,套接字,互斥量等)的技术。

        在对象构造时获得资源,使得堆资源的控制在对象的生命周期内都有效,最后在对象析构的时候释放资源。

        实际上我们是将资源的管理托管给了一个对象。这样做有两个好处:

  1. 不需要显示的释放资源。
  2. 资源的控制在对象生命周期内都有效。

利用RAII实现一个最简单的智能指针。

#pragma once

#include<iostream>

using namespace std;
template<class T>
class SmatrPrt
private:
	T* _ptr;
public:
	//将外面申请的资源,托管给类的成员
	SmatrPrt(T* ptr = nullptr)
		:_ptr(ptr)
	

	//在对象析构时,自动释放资源
	~SmatrPrt()
		if (_ptr)
			cout << "delete ptr" << endl;
			delete _ptr;
		

	
;
#include "SmartPtr.h"

void test()
    //将申请的资源托管给了sp对象
    //当对象空间释放会调用析构函数,释放资源
	SmatrPrt<int> sp(new int);



int main()

	test();

	system("pause");
	return 0;

        1.2 原理

        智能指针的原理利用了RAII思想,并且还需要让智能指针具有指针的功能。即,需要重载operator*和operator->的函数。

        之后在使用指针时,只需要像指针一样控制对象即可。

#pragma once

#include<iostream>

using namespace std;
template<class T>
class SmatrPrt
private:
	T* _ptr;
public:
	//将外面申请的资源,托管给类的成员
	SmatrPrt(T* ptr = nullptr)
		:_ptr(ptr)
	

	T& operator*()
		return *_ptr;
	
	T* operator->()
		return _ptr;
	


	//在对象析构时,自动释放资源
	~SmatrPrt()
		if (_ptr)
			cout << "delete ptr" << endl;
			delete _ptr;
		

	
;

#include "SmartPtr.h"

void test()
	SmatrPrt<int> sp(new int);
    //像指针一样操作对象
	*sp = 20;
	cout << *sp << endl;



int main()

	test();

	system("pause");
	return 0;

总结指针指针:

  1. 利用RAII思想,实现自动释放资源。
  2. 重载operator*和operator->,使其具有和指针一样的行为和功能。

二.智能指针的分类

        上面的代码有一个bug:

原因:

        用sp1拷贝构造sp2,由于是浅拷贝。会使得两个对象的成员变量指向同一块空间。两个对象在析构时,导致一块空间释放了两次。所以程序会奔溃。

随着C++的发展,有三个解决方案,一个方案对应着一种智能指针。

  1. 将指针的管理权转移给另外一个对象。对应C++98的auto_ptr。
  2. 防止拷贝。对应C++11的unique_ptr。
  3. 引用计数。对应C++11的shared_ptr。

注意:智能指针都包含在memory的库中,要使用智能指针必须包含这个库。

        2.1 auto_ptr介绍

        auto_ptr的原理是:将资源的管理权由一个对象转移给另外一个对象。

         但是这样会有一个问题,如果重新访问ap1,就会出现问题。这种方式在实际在编程中用得少。

模拟实现auto_ptr:

注意:赋值防止自己给自己赋值,还需要释放当前对象以前的资源。

template<class T>
class AutoPtr
private:
	T* _ptr;
public:
	
	AutoPtr(T* ptr = nullptr)
		:_ptr(ptr)
	

	//拷贝构造
	//将ap资源的管理权交当前对象
	AutoPtr(const AutoPtr<T>& ap)
		_ptr = ap._ptr;
		ap._ptr = nullptr;
	

	AutoPtr<T>& operator=(const AutoPtr<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;
	


	//在对象析构时,自动释放资源
	~AutoPtr()
		if (_ptr)
			cout << "delete ptr" << endl;
			delete _ptr;
		

	
;

        2.2 unique_pt介绍

        针对auto_ptr的不足,C++11根据自己的一些语法,设计了一种更靠谱的智能指针,unique_ptr。

        原理:直接简单粗暴,将构造和拷贝构造直接禁止编译器默认生成。

 unique_ptr模拟实现: 

#pragma once

#include<iostream>

using namespace std;

template<class T>
class UniquePtr
private:
	//C++98,将拷贝构造和赋值,显示声明,设为私有
	//外部无法调用
	UniquePtr(UniquePtr<T>& up);
	UniquePtr<T>& operator=(UniquePtr<T>& up);


	T* _ptr;
public:
	//将外面申请的资源,托管给类的成员
	UniquePtr(T* ptr = nullptr)
		:_ptr(ptr)
	

	//利用C++11语法,禁止编译器默认生成
	UniquePtr(UniquePtr<T>& up) = delete;
	UniquePtr<T>& operator=(UniquePtr<T>& up) = delete;


	T& operator*()
		return *_ptr;
	
	T* operator->()
		return _ptr;
	


	//在对象析构时,自动释放资源
	~UniquePtr()
		if (_ptr)
			cout << "delete ptr" << endl;
			delete _ptr;
		

	
;

         2.3 shared_ptr介绍

        shared_ptr原理:在类中增加一个成员变量用来计数。每次调用一次拷贝构造和赋值重载函数,即每增加一个管理者,计数加1。没析构一个管理者对象,计数器减1,直到减为0,才真正释放资源。

  • shared_ptr内部,给每一份资源维护了一个计数器,用来记录该份资源被几个对象共享。
  • 在对象被销毁,也就是调用析构时,说明不在管理该资源,计数器减1。
  • 如果引用计数等于0,说明当前对象是最后一个使用该资源的对象,必须释放资源。
  • 当引用计数不等于0,说明还有对象在管理资源,不能释放资源。

计数器如何设置:

        由于当前计数器属于同一资源的对象,可以将计数器设计成一个指针。

        不能设计成引用,引用要引用外部变量,需要外部管理。也不能设计成静态成员变量,静态成员变量,属于整个类,如果当前类要同时管理其它资源计数器就乱了。

线程安全问题:

        由于引用了一个计数器。计数器自增(++),自减(--)和判断的行为不是原子的。当多个线程进入,会导致线程安全问题。从而导致资源没有释放,造成内存泄漏。

        为了保证线程安全,可以在shared_ptr类中,再增加一把锁。由于同一份资源的对象要看到同意把锁,所以,锁也需要声明成指针类型。

shared_ptr模拟实现:

        注意:

  1. 在赋值重载函数中,首先需要判断是否是自己给自己赋值;
  2. 其次,不能直接释放被赋值对象的资源,需要先将计数器减1(当前对象不管理了),当计数器等于0,说明没有其它对象管理当前资源,才将资源释放。
  3. 在析构函数中,也是需要将计数器减为0 了,才能将资源释放。
  4. 释放资源时注意,需要先解锁了,再释放锁的资源。
  5. 在计数器自增,自减和判断前需要加锁。
#include<mutex>
template<class T>
class SharedPtr
private:

	T* _ptr;
	int* _count;
	mutex* _mt;//防止多线程安全问题

private:
	void AddCount()
		_mt->lock();
		(*_count)++;
		_mt->unlock();
	

	void ReleasePtr()
		//是否需要删除锁
		bool flag = true;
		//加锁,防止线程安全
		_mt->lock();
		(*_count)--;
		if ((*_count) == 0)
			delete _ptr;
			delete _count;
			flag = false;//锁要在外面删除,需要解锁
		
		_mt->unlock();
		//删除锁
		if (flag == false)
			cout << "delete" << endl;
			delete _mt;
		
	

public:
	//将外面申请的资源,托管给类的成员
	SharedPtr(T* ptr = nullptr)
		:_ptr(ptr)
		, _count(new int(1))//申请一份资源,初始化为1
		, _mt(new mutex)
	

	SharedPtr(SharedPtr<T>& sp)
		_ptr = sp._ptr;
		_count = sp._count;
		_mt = sp._mt;
		AddCount();
	
	SharedPtr<T>& operator=(SharedPtr<T>& sp)
		//防止自己给自己赋值
		if (this != &sp)
			//是否管理资源
			if (_ptr)
				ReleasePtr();
			

			_ptr = sp._ptr;
			_count = sp._count;
			_mt = sp._mt;

			AddCount();

		
		return *this;
	

	T& operator*()
		return *_ptr;
	
	T* operator->()
		return _ptr;
	


	//在对象析构时,自动释放资源
	~SharedPtr()

		ReleasePtr();

	
;

shared_ptr缺陷:

循环引用问题:

一种情况:

         当循环引用时,shared_ptr会出现资源没有释放的问题。

解决方案:

        C++11增加了一个weak_ptr专门来解决shared_ptr循环引用的问题。

        原理是:weak_ptr的成员变量是shared_ptr,不在具有RAII的思想,只是具有指针的功能,也就是,不会释放资源,也不会将计数器计数。

        使用weak_ptr前提是,知道是循环引用了。

template<class T>
class WeakPtr
private:
	T* _wptr;
public:

	WeakPtr(T* wptr = nullptr)
		_wptr = wptr;
	
	//用shared_ptr构造
	WeakPtr(const SharedPtr<T>& sp)
		_wptr = sp.GetPtr();
	

	WeakPtr<T>& operator=(const SharedPtr<T>& sp)
		_wptr = sp.GetPtr();
		return *this;
	
	//具有指针的功能
	T* operator->()
		return &_wptr
	

	T& operator*()
		return *_wptr;
	
;

 上面问题的改造:

  删除器:

        shared_ptr类里默认的在析构函数里释放资源使用的是delete。但是当我们申请出来的不是一个对象,而是多个对象时:

 于是C++11增加了一个删除器,可以根据申请对象的不同,设计删除动作,传入shared_ptr中。

实际用法是,写一个仿函数类(写一个类,从在operator()函数,函数实现是需要删除资源的动作),实例化对象,传入shared_ptr中。

template<class T>
class Del
public:
	void operator()(T* ptr)
		delete[] ptr;
	
;


class DelFle
public:
	void operator()(FILE* ptr)
		fclose(ptr);
	
;

void test()
	Del<int> d;
	shared_ptr<int> sp1(new int[10], d);
	
	DelFle df;
	shared_ptr<FILE> sp2(fopen("text.txt", "w"), df);

	

 

c++11智能指针(代码片段)

C++11智能指针前言:  近来,学习STL,突然发现有智能指针,做了一周的学习(工作之外的时间),断断续续的学习,特此做下记录。诞生的原因:   为了防止内存泄露,和二次释放的问题。无非就是嫌弃自己管理内存太费... 查看详情

bingc++(智能指针类型转化c++11)(代码片段)

上一篇目录标题智能指针c++11解决auto_ptr(unique_ptr)shared_ptr解决循环引用RAII扩展学习类型转化static_castreinterpret_castconst_castdynamic_castexplicittypeidc++11列表初始化变量类型推导范围for循环final与override智能指针 查看详情

bingc++(智能指针类型转化c++11)(代码片段)

上一篇目录标题智能指针c++11解决auto_ptr(unique_ptr)shared_ptr解决循环引用RAII扩展学习类型转化static_castreinterpret_castconst_castdynamic_castexplicittypeidc++11列表初始化变量类型推导范围for循环final与override智能指针 查看详情

c++11——智能指针(代码片段)

目录前言一.智能指针的原理    1.1RAII思想    1.2原理二.智能指针的分类    2.1auto_ptr介绍    2.2unique_pt介绍     2.3shared_ptr介绍前言    由于C++没有GC(垃圾回收器),程序员从堆上申请的资源,打开的文件... 查看详情

c++11---智能指针(代码片段)

在使用C++编程时,要求使用malloc/new申请出来的空间必须使用free/delete进行释放,如果程序员没有对使用malloc/new申请的空间在使用free/delete进行释放,则可能会造成内存泄露问题。但是,在C++中有些情况... 查看详情

c++11:智能指针(代码片段)

RAIIRAII,全称资源获取即初始化(英语:ResourceAcquisitionIsInitialization)。RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构... 查看详情

c++-智能指针(代码片段)

智能指针智能指针的使用及原理RAII智能指针的原理std::auto_ptrstd::unique_ptrstd::shared_ptrstd::shared_ptr的循环引用、std::weak_ptrC++11和boost中智能指针的关系智能指针的使用及原理malloc出来的空间,没有进行释放,存在内存... 查看详情

c++-智能指针(代码片段)

智能指针智能指针的使用及原理RAII智能指针的原理std::auto_ptrstd::unique_ptrstd::shared_ptrstd::shared_ptr的循环引用、std::weak_ptrC++11和boost中智能指针的关系智能指针的使用及原理malloc出来的空间,没有进行释放,存在内存... 查看详情

智能指针(代码片段)

https://blog.csdn.net/gettogetto/article/details/66968307http://blog.csdn.net/zy19940906/article/details/50470087 本次讨论:c++11之前的auto_ptr;c++11新加的unique_ptr,shared_ptr以及weak_ptr。   头 查看详情

[c++]智能指针(代码片段)

目录1.为什么需要智能指针?2.RAII(资源获取及初始化)2.1RAII方式的原理2.2重大问题2.3智能指针原理3.C++98auto_ptr(不要用)3.1解决浅拷贝的方式一3.2解决浅拷贝的方式二4.C++11unique_ptr4.1管理的资源多样如何释放4.2模拟实现5.C&... 查看详情

[c++]智能指针(代码片段)

目录1.为什么需要智能指针?2.RAII(资源获取及初始化)2.1RAII方式的原理2.2重大问题2.3智能指针原理3.C++98auto_ptr(不要用)3.1解决浅拷贝的方式一3.2解决浅拷贝的方式二4.C++11unique_ptr4.1管理的资源多样如何释放4.2模拟实现5.C&... 查看详情

智能指针11(代码片段)

智能指针智能指针存在的必要性智能指针的使用及原理C++98中的智能指针C++11中的智能指针C++11中unique_ptr指针C'++11中的shared_ptrshared_ptr循环引用所引发的问题。如何解决shared_ptr所造成的循环调用所引起... 查看详情

shared_ptr智能指针模板类的简单实现(c++11)(代码片段)

...深入理解stl,还是对c++11知识的练习,就从智能指针开始吧。另外,c++11让c++程序变得简洁优雅了许多,对这门语言有点爱不释手了。智能指针原理通过使用引用计数的方式来自动的对动 查看详情

c++11:智能指针(代码片段)

智能指针是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保在离开指针所在作用域时,自动正确地销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数。每使用它一... 查看详情

深入学习c++--智能指针(代码片段)

1.几种智能指针1.auto_ptr:c++11中推荐不使用他2.shared_ptr:每添加一次引用就+1,减少一次引用,就-1;做到指针进行共享3.unique_ptr:一个指针同时只能有一个使用者使用4.weaked_ptr:与shared_ptr搭配使用1.1shared_ptr参考:https://zh.cppreference.com... 查看详情

[c++11新特性]智能指针详解(代码片段)

...同时也更安全)地使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象。智能指针的行为类似常规指针,区别在于它负责自动释放所指向的对象。这两种智能指针的区别在于管理底层指针的方式:shared_ptr允许多... 查看详情

bingc++(智能指针类型转化c++11)(代码片段)

上一篇目录标题智能指针c++11解决auto_ptr(unique_ptr)shared_ptr解决循环引用RAII扩展学习类型转化static_castreinterpret_castconst_castdynamic_castexplicittypeidc++11列表初始化变量类型推导范围for循环final与override智能指针新增... 查看详情

c++|项目开发—智能指针回顾(代码片段)

智能指针     C++的智能指针: auto_ptr,shared_ptr,weak_ptr,unique_ptr ,auto_ptr已经被c++11弃用,被更安全的shared_ptr所替代。在C++中使用new关键字开辟的内存必须被手动delete掉,不然就会导致内存的泄漏... 查看详情