首页 > 其他分享 >智能指针

智能指针

时间:2024-08-29 19:50:53浏览次数:6  
标签:cout 智能 shared include ------------------------- ptr 指针

智能指针

在C++中管理动态内存可以使用new和delete,但通过这种方式得到的指针(裸指针)是容易忘记释放的进而导致内存泄漏。因此C++标准中提供了智能指针shared_ptrweak_ptrunique_ptr来进行动态内存的管理。智能指针的设计满足了RAII(Resource Acquisition Is Initialization)的特性,即资源获取即初始化,这使得程序猿能够节省更多找内存泄漏的时间去实现需要的功能。

在这些智能指针中shared_ptr和weak_ptr是一起用于管理shared_ptr智能指针对象的,而unique_ptr智能指针是单独管理动态内存的。

智能指针头文件

这几个智能指针模板类的头文件都是memory

shared_ptr

shared_ptr是共享型智能指针,其内部有引用计数器,这使得多个shared_ptr对象能指向同一个对象并且仅当引用计数为0时释放对象的内存。

shared_ptr初始化

  1. 使用new初始化
shared_ptr<int> test1(new int(100))
  1. 使用更安全的函数模板make_shared(params)来初始化
shared_ptr<int> test2 = make_shared<int>(100);

shared_ptr常用操作

						成员函数
[shared_ptr<T> object]------------[use_count()]查看引用计数
          |                  |-----[unique()]查看智能指针是否独占某个指向的对象
          |                  |-----[reset()/reset(param)]置空或指向其他对象
          |                  |-----[*]*运算符重载,用于模拟指针相关的操作
          |                   -----[get()]获取裸指针
          |  删除器
           -----------shared<T>(new T, deleter)初始化shared_ptr指定删除器,当引用计数为0后调用删除器函数
use_count()成员函数

查看引用计数,使用use_count()成员函数,这个函数主要用于调试目的,效率可能不高。当使用shared_ptr进行拷贝构造,拷贝赋值运算符处理后,引用计数都会+1。

举例:

#include <iostream>
#include <memory>
int main()
{
	using namespace std;
	cout << "-------------------------" << endl;
	shared_ptr<int> test1 = make_shared<int>(100);
	cout << "初始化shared_ptr,引用计数为:" << test1.use_count() << endl;
	cout << "-------------------------" << endl;
	shared_ptr<int> test2 = test1;
	cout << "使用拷贝构造,引用计数为:" << test1.use_count() << endl;
	cout << "-------------------------" << endl;
	shared_ptr<int> test3;
	test3 = test1;
	cout << "使用拷贝赋值运算符,引用计数为:" << test1.use_count() << endl;
	cout << "-------------------------" << endl;
	return 0;
}

结果:

-------------------------
初始化shared_ptr,引用计数为:1
-------------------------
使用拷贝构造,引用计数为:2
-------------------------
使用拷贝赋值运算符,引用计数为:3
-------------------------
unique()成员函数

这个函数是查看shared_ptr对象是否独占某个对象,如果是则返回true,否则返回false。

举例

#include <iostream>
#include <memory>
int main()
{
	using namespace std;
	cout << "-------------------------" << endl;
	shared_ptr<int> test1 = make_shared<int>(100);
	if (test1.unique() == true)
	{
		cout << "test1 is unique" << endl;
	}
	cout << "-------------------------" << endl;
	return 0;
}

结果:

-------------------------
test1 is unique
-------------------------
reset()与reset(param)成员函数

​ reset成员函数有两个重载,分别是带参重载和不带参数重载。带参重载的reset使智能指针指向新的对象,并使得原先对象的引用计数-1;不带参数的reset使原先指向对象的引用计数-1,并将智能指针对象置空。

举例

#include <iostream>
#include <memory>
int main()
{
	using namespace std;
	cout << "-------------------------" << endl;
	shared_ptr<int> test1 = make_shared<int>(100);
	test1.reset();
	if (test1 == nullptr)
	{
		cout << "智能指针test1置空" << endl;
	}
	cout << "-------------------------" << endl;
	test1.reset(new int(200));
	if (test1 != nullptr)
	{
		cout << "智能指针test1通过reset(param)成员函数指向新的对象" << endl;
	}
	return 0;
}

结果

-------------------------
智能指针test1置空
-------------------------
智能指针test1通过reset(param)成员函数指向新的对象
*解引用

*是被重载的运算符,通过使用它可以对指针解引用。

举例

#include <iostream>
#include <memory>
int main()
{
	using namespace std;
	cout << "-------------------------" << endl;
	shared_ptr<int> test1 = make_shared<int>(100);
	cout << "*解引用: " << *test1 << endl;
	cout << "-------------------------" << endl;
	return 0;
}

结果

-------------------------
*解引用: 100
-------------------------
get()成员函数

get()成员函数返回保护的裸指针,在获取到这个裸指针后不要用它去初始化新的shared_ptr对象,因为初始化新的智能指针后,引用计数器变为多个,当多个引用计数器=0时会释放多次内存导致程序出错。

举例

#include <iostream>
#include <memory>
#include <typeinfo>
int main()
{
	using namespace std;
	cout << "-------------------------" << endl;
	shared_ptr<int> test1 = make_shared<int>(100);
	cout << "test1的类型: " << typeid(test1).name() << endl;
	cout << "-------------------------" << endl;
	auto ptr = test1.get();
	cout << "ptr1的类型: " << typeid(ptr).name() << endl;
	return 0;
}

结果

-------------------------
test1的类型: class std::shared_ptr<int>
-------------------------
ptr1的类型: int *
swap()成员函数

swap()用于交换两个智能指针指向的对象。

举例

#include <iostream>
#include <memory>
#include <string>
int main()
{
	using namespace std;
	cout << "-------------------------" << endl;
	shared_ptr<string> ptr1 = make_shared<string>("shared_ptr1");
	shared_ptr<string> ptr2 = make_shared<string>("shared_ptr2");
	ptr1.swap(ptr2);
	cout << "*ptr1: " << *ptr1 << endl;
	cout << "*ptr2: " << *ptr2 << endl;
	cout << "-------------------------" << endl;
	return 0;
}

结果

-------------------------
*ptr1: shared_ptr2
*ptr2: shared_ptr1
-------------------------
删除器

shared_ptr删除器就是使用程序猿自己写的删除器函数或者lambda表达式在智能指针对象引用计数为0时调用。

举例

#include <iostream>
#include <memory>
using namespace std;

/*
* @brief 删除器函数
*/
void deleter(int* p)
{
	cout << "调用删除器函数" << endl;
	delete p;
}

int main()
{
	cout << "-------------------------" << endl;
	shared_ptr<int> ptr(new int(100), deleter);
	//引用计数=0,调用删除器
	ptr.reset();
	cout << "-------------------------" << endl;
	ptr = shared_ptr<int>(new int(200), [](int* p) 
		{
			cout << "lambda表达式删除器" << endl;
			delete p; 
		});
	//引用计数=0,调用删除器
	ptr.reset();
	cout << "-------------------------" << endl;
	return 0;
}

结果

-------------------------
调用删除器函数
-------------------------
lambda表达式删除器
-------------------------
注意事项

当使用shared_ptr来管理数组对象时要写删除器,因为shared_ptr默认的删除器不会释放数组对象。当遇到这种情况时有三种方式能正常释放内存

举例

#include <iostream>
#include <memory>
using namespace std;
int main()
{
	cout << "-------------------------" << endl;
	//方法1.用lambda表达式或者删除器函数来处理
	shared_ptr<int> ptr1(new int[10], [](int* p)
		{
			cout << "释放int数组的内存" << endl;
			delete[]p;
		});
	//引用计数=0
	ptr1.reset();
	cout << "-------------------------" << endl;
	//方法2.调用标准库的default_delete
	shared_ptr<int> ptr2(new int[10], default_delete<int[]>());
	cout << "-------------------------" << endl;
	//方法3.在尖括号中也加上[],这样使用默认删除器也能正常释放内存
	shared_ptr<int[]> ptr3(new int[10]);

	return 0;
}

结果

-------------------------
释放int数组的内存
-------------------------
-------------------------

weak_ptr

weak_ptr是用来辅助shared_ptr工作的,weak_ptr通常用于绑定shared_ptr指向的对象并获取强引用计数及初始化新的shared_ptr对象。

weak_ptr初始化

初始化weak_ptr对象有两种方式,一种是用shared_ptr对象初始化,另一种是用另一个weak_ptr对象初始化 。

举例

#include <iostream>
#include <memory>
using namespace std;
int main()
{
	shared_ptr<int> s_ptr = make_shared<int>(100);
	cout << "-------------------------" << endl;
	weak_ptr<int> w_ptr1(s_ptr);
	cout << "使用shared_ptr对象初始化的weak_ptr: " << *(w_ptr1.lock()) << endl;
	cout << "-------------------------" << endl;
	weak_ptr<int> w_ptr2(w_ptr1);
	cout << "使用weak_ptr初始化的weak_ptr: " << *(w_ptr2.lock()) << endl;

	return 0;
}

结果

-------------------------
使用shared_ptr对象初始化的weak_ptr: 100
-------------------------
使用weak_ptr初始化的weak_ptr: 100

weak_ptr常用操作

				常用函数
[weak_ptr]------------------[use_count()] 强引用计数
                    |-------[expired()] 判断强引用计数是否为0
                    |-------[reset()] 重置weak_ptr对象,原对象弱引用计数-1
                     -------[lock()] 获取监视的shared_ptr,用于初始化shared_ptr对象

use_count()成员函数

weak_ptr中的use_count()成员函数的功能和shared_ptr中的同名函数一样,都是用于获取shared_ptr的引用计数。

举例

#include <iostream>
#include <memory>
using namespace std;
int main()
{
	shared_ptr<int> s_ptr = make_shared<int>(100);
	shared_ptr<int> s_ptr2 = s_ptr;
	cout << "-------------------------" << endl;
	weak_ptr<int> w_ptr(s_ptr);
	cout << "当前shared_ptr中的引用计数为: " << s_ptr.use_count() << endl;
	cout << "当前weak_ptr中的引用计数为: " << w_ptr.use_count() << endl;
	cout << "-------------------------" << endl;

	return 0;
}

结果

-------------------------
当前shared_ptr中的引用计数为: 2
当前weak_ptr中的引用计数为: 2
-------------------------

expired()成员函数

weak_ptr对象是用于监视shared_ptr对象的,weak_ptr中的expired用于判断监视的shared_ptr对象中的引用计数是否为0,如果计数=0则返回true,反之返回false。

举例

#include <iostream>
#include <memory>
using namespace std;
int main()
{
	shared_ptr<int> s_ptr = make_shared<int>(100);
	cout << "-------------------------" << endl;
	weak_ptr<int> w_ptr(s_ptr);
	if (!w_ptr.expired())
	{
		cout << "此时shared_ptr引用计数不等于0" << endl;
	}
	cout << "-------------------------" << endl;
	s_ptr.reset();
	if (w_ptr.expired())
	{
		cout << "此时shared_ptr引用计数为0" << endl;
	}
	return 0;
}

结果

-------------------------
此时shared_ptr引用计数不等于0
-------------------------
此时shared_ptr引用计数为0

reset()成员函数

reset()成员函数用于将weak_ptr对象置空,不影响指向之前对象的强引用数量,但弱引用数量会减1。

lock()成员函数

lock()成员函数用于获取监视的shared_ptr智能指针,可以用lock()函数来初始化新的shared_ptr对象。

举例

#include <iostream>
#include <memory>
using namespace std;
int main()
{
	shared_ptr<int> s_ptr = make_shared<int>(100);
	cout << "-------------------------" << endl;
	weak_ptr<int> w_ptr(s_ptr);
	shared_ptr<int> s_ptr2 = w_ptr.lock();
	cout << "通过weak_ptr获取的shared_ptr对象解引用的结果为: " << *s_ptr << endl;
	cout << "-------------------------" << endl;
	return 0;
}

结果

-------------------------
通过weak_ptr获取的shared_ptr对象解引用的结果为: 100
-------------------------

shared_ptr和weak_ptr的内部细节

当使用创建了shared_ptr对象并让其监视了一个T类型的对象后,控制块就被shared_ptr对象创建了出来,而shared_ptr对象内部的两个指针就分别指向T对象和对应的控制块对象,当用shared_ptr对象初始化weak_ptr对象后,weak_ptr对象也用两个指针分别指向T和控制块对象。其中强引用计数是指向T对象的shared_ptr对象总数,而弱引用计数是指向T对象的weak_ptr对象总数。

使用shared_ptr和weak_ptr的注意事项

在成员函数中要用shared_from_this()函数来返回this指针

这一部分我建议大家先看错误案例分析及如何解决后再去记使用方法。

enable_shared_from_this及shared_from_this()用法

//头文件memory
#include <memory>
class object: public std::enable_shared_from_this<object>
{
  shared_ptr<object> getself()
  {
      return shared_from_this();
  }
};

错误案例及分析

//成员函数返回this的错误做法
#include <iostream>
#include <memory>
using namespace std;
class test
{
public:
	shared_ptr<test> getThis()
	{
		return shared_ptr<test>(this);
	}
};
int main()
{

	cout << "-------------------------" << endl;
	shared_ptr<test> s_ptr = make_shared<test>();
	shared_ptr<test> s_ptr2 = s_ptr->getThis();
	cout << "s_ptr的引用计数为:" << s_ptr.use_count() << endl;
	cout << "s_ptr2的引用计数为:" << s_ptr2.use_count() << endl;
	cout << "-------------------------" << endl;
	return 0;
}

结果

-------------------------
s_ptr的引用计数为:1
s_ptr2的引用计数为:1
-------------------------

17行创建了智能指针对象s_ptr,而在18行创建s_ptr2时问题出现了。this指针是裸指针,这就导致在使用getThis()时创建了另一个新的引用计数的shared_ptr对象,这就使s_ptr和s_ptr2对象的引用计数不是同一个,最终导致在销毁s_ptr和s_ptr2时会导致释放两次内存报错。

如何解决这个错误?

在C++标准库中提供了一种类模板enable_shared_from_this

标签:cout,智能,shared,include,-------------------------,ptr,指针
From: https://www.cnblogs.com/code4log/p/18387458

相关文章

  • 工信部人工智能证书在哪报名?报名入口!
    证书出台背景:为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署要求,深入实施人才强国战略和创新驱动发展战略,加强全国数字化人才队伍建设,持续推进人工智能从业人员能力培养和评价,工业和信......
  • 小红书自动化智能运营,6款RPA机器人免费下载
    之前我们分享了 7款抖音 & 9款微信 RPA机器人,很多爪爪们都开始用了起来......
  • 实现一个通过调用openai4.0的智能聊天系统,支持上传图片(这里是通过websocket返回流式效
    <template><divclass="chatInfor"><divclass="chatInfor-content"><el-scrollbarheight="97%"id="chatBox"ref="scrollbarRef"v-loading="loading"wi......
  • 1200余份数字化建设方案:企业数字化转型、数据治理、数据中台、主数据、数字工厂、智能
    1200余份数字化建设方案:企业数字化转型、数据治理、数据中台、主数据、数字工厂、智能制造、数字乡村、智慧园区等建设方案及报告。一、 数字化建设方案WORD格式1、企业数字化建设企业数字化建设是指将传统企业的业务、流程、管理等方面通过信息技术的应用进行改造......
  • 深入浅出LLamaSharp:打造智能.NET应用,不需GPU也能玩转LLaMA模型
            在如今的.NET社区中,机器学习和人工智能的应用越来越普遍。今天我要给大家推荐一个名叫LLamaSharp的开源项目。这是llama.cpp的C#/.NET绑定,提供了高级的API,使得我们能在本地设备上使用C#/.NET推理LLaMA模型,并且部署它。        LLamaSharp支持在Windo......
  • AutoGen:微软AI多智能体会话框架的新变革
            在人工智能发展的今天,对话系统和自然语言处理技术的创新日新月异。微软推出了名为AutoGen的框架,它标志着在使用大型预训练语言模型(LLM)开发应用程序方面的一次飞跃。AutoGen不仅因其技术突破而受到业界的广泛关注,并且在短短的时间里,已经被TheSequence评为2023年......
  • ★♛★指针(重难点)合集
    1.介绍地址内存:所有程序的运行在内存中用CheatEngine查看任意程序的内存(16进制):显示大量的数据想要定位某个数字,需要知道地址(类比二维坐标)如F8的地址为00BCB900+08(偏移量),所以是00BCB908(偏移)ctrl+G则有内存单元的说明:打开计算器点三道杠,选程序员显然一......
  • 软硬件全开源智能手表,可全面高精度采集生命体征数据,进行健康检测。(HealthyPi Move)
    HealthyPiMove是一款开放式硬件设备,可让您高精度地跟踪所有生命体征。它不仅仅是另一款带有心率监测器的智能手表,它还是手腕上的完整生命体征监测和记录设备,可以测量心电图(ECG)、光电容积脉搏波(PPG)、SpO₂、血压(基于手指)、EDA/GSR、心率变异性(HRV)、呼吸频率,甚至体温......
  • Python 中常用的人工智能库和工具
    在Python中,有许多强大的人工智能库和工具,它们为开发各种人工智能应用提供了有力的支持。以下是一些常用的人工智能库和工具介绍:一、机器学习库Scikit-learnScikit-learn是一个广泛应用于机器学习的Python库。它提供了各种经典的机器学习算法,包括分类、回归、聚类等......
  • 选择全能还是专业?人工智能的未来之路
    ​探索全能型AI与专业型AI的融合与演进在人工智能的宏伟蓝图中,我们面临着一个关键的抉择:是追求能够执行任何智能任务的全能型AI,还是专注于特定领域的专业型AI?本文将深入探讨这两种AI的特点,并预测它们在未来发展中的角色。一、AI模型的全面评估和比较全能型AI:人类智能的镜像专......