返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++11 智能指针的具体使用
  • 612
分享到

C++11 智能指针的具体使用

2024-04-02 19:04:59 612人浏览 独家记忆
摘要

目录智能指针的原理RaiI智能指针的原理auto_ptr1.auto_ptr的使用及问题unique_ptrshared_ptrshared_ptr的循环引用智能指针的原理 RAII

智能指针的原理

RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。 借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

我们使用RAII的思想设计SmartPtr类:


template <class T>
class SmartPtr
{
public:
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

    ~SmartPtr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

private:
	T* _ptr;
};

智能指针的原理

上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容 ,因此:SmartPtr模板类中还得需要将* 、->重载下,才可让其像指针一样去使用。


template <class T>
class SmartPtr
{
public:
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

    ~SmartPtr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

private:
	T* _ptr;
};

智能指针使用:

在这里插入图片描述

总结智能指针的原理:

  • RAII特性
  • 重载operator*和opertaor->,具有像指针一样的行为。

auto_ptr

1.auto_ptr的使用及问题

auto_ptr的头文件#include<memory>

auto_ptr的使用:

在这里插入图片描述

在这里插入图片描述

为什么此时访问sp的成员时会报错呢?我们来看看它们的地址。

在这里插入图片描述

我们发现在拷贝构造之后,sp管理的地址为空,而sp1管理的地址是之前sp所管理的地址,管理权发生了转移。那么上面所说的报错也很容易想通,因为sp管理的地址为空,不能进行访问。

auto_ptr的问题:当对象拷贝或者赋值后,管理权进行转移,造成前面的对象悬空。auto_ptr问题是非常明显的,所以实际中很多公司明确规定了不能使用auto_ptr。

auto_ptr的实现原理:管理权转移的思想,下面简化模拟实现了一份AutoPtr来了解它的原理:


template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr)
		:_ptr(ptr)
	{}

	//拷贝:管理权转移
	AutoPtr(AutoPtr<T> &sp)
		:_ptr(sp._ptr)
	{
		sp._ptr = nullptr;
	}

	//赋值:管理权转移
	AutoPtr& operator=(AutoPtr<T> &sp)
	{
		if (this != &sp)
		{
			if (_ptr)
				delete _ptr;
			_ptr = sp._ptr;
			sp._ptr = nullptr;
		}
		return *this;
	}

	~AutoPtr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

private:
	T* _ptr;
};

unique_ptr

为了解决拷贝或者赋值时管理权转移的问题,出现了unique_ptr。

unique_ptr解决问题的方式非常粗暴:防拷贝,也就是不让赋值和拷贝

unique_ptr的使用:

在这里插入图片描述

unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原理:


template<class T>
class UniquePtr
{
public:

	UniquePtr(T* ptr)
		:_ptr(ptr)
	{}

	// c++11防拷贝的方式:delete
	UniquePtr(const UniquePtr<T> &) = delete;

	UniquePtr& operator=(const UniquePtr<T>&) = delete;

	T* operator->()
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

	~UniquePtr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

private:

	//C++98防拷贝的方式:只声明不实现+声明成私有
	//UniquePtr(UniquePtr<T> const &);
	//UniquePtr& operator=(UniquePtr<T> const &);

	T* _ptr;
};

shared_ptr

c++11中提供更靠谱的并且支持拷贝的shared_ptr

shared_ptr的使用

在这里插入图片描述

shared_ptr中拷贝与赋值都是没有问题的。

shared_ptr的原理

  • shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  • 对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一
  • 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
  • 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

shared_ptr中成员函数:use_count(对象数据的引用计数)

示例:

在这里插入图片描述

示例详解:

在这里插入图片描述

利用引用计数简单的实现SharedPtr,了解原理:


template<class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		,_count(new int(1))
	{}

	SharedPtr(const SharedPtr<T> &sp)
		:_ptr(sp._ptr)
		,_count(sp._count)
	{
		//计数器累加
		++(*_count);
	}

	SharedPtr& operator=(const SharedPtr<T> &sp)
	{
		//判断管理的是否是同一份资源
		if (_ptr != sp._ptr)
		{
			//计数-1,判断之前管理的资源是否需要释放
			if ((--(*_count)) == 0)
			{
				delete _ptr;
				delete _count;
			}
			
			_ptr = sp._ptr;
			_count = sp._count;

			//计数器累加
			++(*_count);
		}
		return *this;
	}

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

	~SharedPtr()
	{
		if (--(*_count) == 0)
		{
			delete _ptr;
			delete _count;
			_ptr = nullptr;
			_count = nullptr;
		}
	}

private:
	T* _ptr;
	int* _count;//给每份资源开辟一个计数器
};

但是还存在一个线程安全的问题:

  1. 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2。这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、- -是需要加锁的,也就是说引用计数的操作是线程安全的。
  2. 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。

这里我们通过加锁来解决线程安全问题:


template<class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		,_count(new int(1))
		,_mutex(new mutex)
	{}

	SharedPtr(const SharedPtr<T> &sp)
		:_ptr(sp._ptr)
		,_count(sp._count)
		,_mutex(sp._mutex)
	{
		//计数器累加
		AddCount();
	}

	SharedPtr& operator=(const SharedPtr<T> &sp)
	{
		//判断管理的是否是同一份资源
		if (_ptr != sp._ptr)
		{
			//计数-1,判断之前管理的资源是否需要释放
			if (SubCount() == 0)
			{
				delete _ptr;
				delete _count;
				delete _mutex;
			}
			
			_ptr = sp._ptr;
			_count = sp._count;
			_mutex = sp._mutex;

				//计数器累加
			AddCount();
		}
		return *this;
	}

	//线程安全的累加器
	int AddCount()
	{
		//加
		_mutex->lock();
		++(*_count);
		_mutex->unlock();
		return *_count;
	}

	int SubCount()
	{
		_mutex->lock();
		--(*_count);
		_mutex->unlock();
		return *_count;
	}

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

	~SharedPtr()
	{
		if (SubCount() == 0)
		{
			delete _ptr;
			delete _count;
			delete _mutex;
			_ptr = nullptr;
			_count = nullptr;
			_mutex = nullptr;
		}
	}
	
private:
	T* _ptr;
	int* _count;//给每份资源开辟一个计数器
	mutex* _mutex; //每一份资源有一个独立的锁
};

shared_ptr的循环引用

循环引用的场景:


struct Listnode
{
	int _data; 
    shared_ptr<ListNode> _prev;
	shared_ptr<ListNode> _next;
	~ListNode() { cout << "~ListNode()" << endl; }
};

void test()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);

	node1->_next = node2;
	node2->_prev = node1;
} 

在这里插入图片描述

node1和node2两个智能指针对象指向两个节点,两个节点的引用计数都是1。node1->next指向node2,node2->prev指向node1,两个节点的引用计数都变成2。程序运行完之后,析构node1和node2,node1和node2所指向的节点引用计数分别减1,但是node1->next指向下面节点,node2->prev指向上面节点,此时,两个节点的引用计数都为1,所以两个节点不能析构。

引用计数为0时,如果要析构node1节点,就先要去析构node1中的自定义结构,然后再析构node1。也就是说node1->next析构了,node2就释放了;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最大作用就是解决shared_ptr的循环引用


struct ListNode
{
	int _data; 
    weak_ptr<ListNode> _prev;
	weak_ptr<ListNode> _next;
	~ListNode() { cout << "~ListNode()" << endl; }
};
void test()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);

	node1->_next = node2;
	node2->_prev = node1;
} 

注意:
weak_ptr不能单独使用,可以用shared_ptr创建


	//weak_ptr错误使用
	weak_ptr<ListNode> node1(new ListNode);

	//weak_ptr正确使用
	shared_ptr<ListNode> node2(new ListNode);
	weak_ptr<ListNode> node3(node2);

到此这篇关于C++11 智能指针的具体使用的文章就介绍到这了,更多相关C++11 智能指针内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++11 智能指针的具体使用

本文链接: https://lsjlt.com/news/133443.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++11 智能指针的具体使用
    目录智能指针的原理RAII智能指针的原理auto_ptr1.auto_ptr的使用及问题unique_ptrshared_ptrshared_ptr的循环引用智能指针的原理 RAII...
    99+
    2024-04-02
  • C++智能指针之shared_ptr的具体使用
    目录std::shared_ptr概念shared_ptr模板类shared_ptr的构造和析构shared_ptr赋值make_shared计数线程安全?enable_shared...
    99+
    2024-04-02
  • C++11智能指针shared_ptr怎么使用
    本篇内容介绍了“C++11智能指针shared_ptr怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!shared_prt的本身是一个...
    99+
    2023-06-19
  • C++11智能指针weak_ptr怎么使用
    本篇内容主要讲解“C++11智能指针weak_ptr怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++11智能指针weak_ptr怎么使用”吧!弱指针weak_ptr看起来更像shar...
    99+
    2023-06-19
  • C++11中的智能指针怎么用
    本篇内容介绍了“C++11中的智能指针怎么用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!函数原型:shared_ptr& oper...
    99+
    2023-06-19
  • C++11中的智能指针和垃圾回收使用
    在C/C++中,我们需要自己管理动态内存区,我们在写代码中可能会出现如下3中内存管理的缺陷 野指针:内存单元已经释放,但是指向它的指针还在使用重复释放:试图是释放已经释放过的内存单元...
    99+
    2023-02-02
    C++11 智能指针和垃圾回收 C++11 智能指针 C++11 垃圾回收
  • C++11智能指针unique_ptr有什么作用
    这篇文章主要讲解了“C++11智能指针unique_ptr有什么作用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++11智能指针unique_ptr有什么作用”吧!软件开发中有很多处理是...
    99+
    2023-06-19
  • C++11智能指针unique_ptr用法使用场景分析
    一、概述 C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:auto_ptr、unique_ptr、shared_ptr...
    99+
    2024-04-02
  • c++智能指针unique_ptr的使用
    目录1.为什么需要unique_ptr2.什么是unique_ptr3.unique_ptr特性4.如何使用unique_ptr4.1简单使用4.2指向数组5.unique_ptr需...
    99+
    2024-04-02
  • C++ this指针和空指针的具体使用
    每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码那么问题是:这—块代码是如何区分那个对象调用自己的呢? C++通过提供特殊的对象指针,this...
    99+
    2024-04-02
  • C++11中的智能指针shared_ptr、weak_ptr源码解析
    目录1、前言2、源码准备3、智能指针概念4、源码解析4.1、shared_ptr解析4.1.1、shared_ptr4.1.2、__shared_ptr4.1.3、__shared_...
    99+
    2024-04-02
  • C++中指针的引用*&的具体使用
    指针和引用形式上很好区别,但是他们似乎有相同的功能,都能够直接引用对象,对其进行直接的操作。 首先,引用不可以为空,但指针可以为空。前面也说过了引用是对象的别名,引用为空——对象都不...
    99+
    2024-04-02
  • C语言结构体指针的具体使用
    目录什么是结构体指针?如何访问结构体成员?如何传递结构体指针作为参数?结构体指针数组在 C语言中,结构体指针是一种非常有用的数据类型,它可以让我们更方便地操作结构体。结构体指针可以指...
    99+
    2023-05-20
    C语言结构体指针
  • C++中怎么使用智能指针
    这篇文章给大家介绍C++中怎么使用智能指针,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、为什么要使用智能指针一句话带过:智能指针就是帮我们C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而...
    99+
    2023-06-20
  • C++中如何使用智能指针
    这期内容当中小编将会给大家带来有关C++中如何使用智能指针,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。内存泄露是C++程序员都头疼的大问题。C++缺乏像JAVA、C#一样,拥有GC这么一项有利的武器,它...
    99+
    2023-06-17
  • C++智能指针shared_ptr怎么使用
    本篇内容介绍了“C++智能指针shared_ptr怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、什么是shared_ptr?C+...
    99+
    2023-06-29
  • C++智能指针shared_ptr
    目录1、什么是shared_ptr?2、shared_ptr支持哪些操作?3、如何创建shared_ptr的实例?4、什么是shared_ptr的引用计数?如何查看?5、shared...
    99+
    2024-04-02
  • C++的智能指针使用实例分析
    今天小编给大家分享一下C++的智能指针使用实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。什么是RAIIRAII(Re...
    99+
    2023-06-29
  • 深入了解C++智能指针的使用
    目录一、C++11智能指针概述二、C++98中的智能指针三、C++11中的智能指针1.unique_ptr2.shared_ptr3.weak_ptr一、C++11智能指针概述 在C...
    99+
    2024-04-02
  • C++智能指针使用实例分析
    这篇文章主要介绍了C++智能指针使用实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++智能指针使用实例分析文章都会有所收获,下面我们一起来看看吧。1.简介程序运行时存在静态空间、栈和堆区,用堆来存储动...
    99+
    2023-06-30
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作