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

cpp智能指针

时间:2025-01-10 13:02:15浏览次数:3  
标签:AA 智能 new cpp shared unique ptr 指针

  普通指针的不足

  • new和new[]的内存需要用delete和deletel]释放。

  • 程序员的主观失误,忘了或漏了释放。

  • 程序员也不确定何时释放。

普通指针的释放

  • 类内的指针,在析构函数中释放。

  • C++内置数据类型,如何释放?

  • new出来的类,本身如何释放?

C++11新增三个智能指针类型

  • unique_ptr

  • shared_ptr

  • weak_ptr

一、智能指针unique_ptr

​        unique_ptr独享它指向的对象,也就是说,同时只有一个unique_ptr指向同一个对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁。unique_ptr是一个类,它的内部维护一个内置的指针,他的析构函数中会delete内置指针。

包含头文件:#include <memory>

template <typename T, typename D = default_delete<T>>
class unique_ptr
{
public:
	explicit unique_ptr(pointer p) noexcept;	// 不可用于转换函数。
	~unique_ptr() noexcept;    
	T& operator*() const;            // 重载*操作符。
	T* operator->() const noexcept;  // 重载->操作符。
	unique_ptr(const unique_ptr &) = delete;   // 禁用拷贝构造函数。
	unique_ptr& operator=(const unique_ptr &) = delete;  // 禁用赋值函数。
	unique_ptr(unique_ptr &&) noexcept;	  // 右值引用。
	unique_ptr& operator=(unique_ptr &&) noexcept;  // 右值引用。
	// ...
private:
	pointer ptr;  // 内置的指针。
};

explicit 关键字

加上explicit后让构造函数不可以被用作转换函数使用。以下调用非法:

AA *p = new AA("xianwu");	//普通指针
unique_ptr<AA> pu2 = p;	

1、基本用法

1)初始化

方法一:推荐

unique_ptr<AA> p0(new AA("西施"));   // 分配内存并初始化。

方法二:

unique_ptr<AA> p0 = make_unique<AA>("西施");  // C++14标准。

方法三(不推荐):原始指针出现如果构造给多个unique_ptr会造成多次释放内存,操作野指针,存在风险。

AA* p = new AA("西施");

unique_ptr<AA> p0(p);         // 用已存在的地址初始化。

2)使用方法

  • 智能指针重载了*和->操作符,可以像使用指针一样使用unique_ptr。
  • 不支持普通的拷贝和赋值。
AA* p = new AA("西施");
unique_ptr<AA> pu2 = p;       // 错误,不能把普通指针直接赋给智能指针。
unique_ptr<AA> pu3 = new AA("西施"); // 错误,不能把普通指针直接赋给智能指针。
unique_ptr<AA> pu2 = pu1;      // 错误,不能用其它unique_ptr拷贝构造。
unique_ptr<AA> pu3;
pu3 = pu1;              // 错误,不能用=对unique_ptr进行赋值。
  • 不要用同一个裸指针初始化多个unique_ptr对象。
  • get()方法返回裸指针。
  • 不要用unique_ptr管理不是new分配的内存。

3)用于函数的参数

  • 传引用(不能传值,因为unique_ptr没有拷贝构造函数)。

  • 裸指针。

4)不支持指针的运算(+、-、++、--)

2、更多技巧 面试就爱考点技巧之类的

1)将一个unique_ptr赋给另一个时,如果源unique_ptr是一个临时右值,编译器允许这样做;如果源unique_ptr将存在一段时间,编译器禁止这样做。一般用于函数的返回值。

unique_ptr<AA> p0;
p0 = unique_ptr<AA>(new AA ("西瓜"));

2)用nullptr给unique_ptr赋值将释放对象,空的unique_ptr==nullptr。

3)release()释放对原始指针的控制权,将unique_ptr置为空,返回裸指针。(可用于把unique_ptr传递给子函数,子函数将负责释放对象)

4)std::move()可以转移对原始指针的控制权。(可用于把unique_ptr传递给子函数,子函数形参也是unique_ptr)

5)reset()释放对象。

void reset(T * _ptr= (T *) nullptr);

pp.reset();    // 释放pp对象指向的资源对象。
pp.reset(nullptr); // 释放pp对象指向的资源对象
pp.reset(new AA("bbb")); // 释放pp指向的资源对象,同时指向新的对象。

6)swap()交换两个unique_ptr的控制权。

void swap(unique_ptr<T> &_Right);

7)unique_ptr也可像普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。

8)unique_ptr不是绝对安全,如果程序中调用exit()退出,全局的unique_ptr可以自动释放,但局部的unique_ptr无法释放。

9)unique_ptr提供了支持数组的具体化版本。

数组版本的unique_ptr,重载了操作符[],操作符[]返回的是引用,可以作为左值使用。

// unique_ptr<int[]> parr1(new int[3]);     // 不指定初始值。
unique_ptr<int[]> parr1(new int[3]{ 33,22,11 }); // 指定初始值。
cout << "parr1[0]=" << parr1[0] << endl;
cout << "parr1[1]=" << parr1[1] << endl;
cout << "parr1[2]=" << parr1[2] << endl;
 
unique_ptr<AA[]> parr2(new AA[3]{string("西施"), string("冰冰"), string("幂幂")});
cout << "parr2[0].m_name=" << parr2[0].m_name << endl;
cout << "parr2[1].m_name=" << parr2[1].m_name << endl;
cout << "parr2[2].m_name=" << parr2[2].m_name << endl;

 

二、智能指针shared_ptr

shared_ptr共享它指向的对象,多个shared_ptr可以指向(关联)相同的对象,在内部采用计数机制来实现,use_count()。

当新的shared_ptr与对象关联时,引用计数增加1。

当shared_ptr超出作用域时,引用计数减1。当引用计数变为0时,则表示没有任何shared_ptr与对象关联,则释放该对象。

 1、基本用法

​        shared_ptr的构造函数也是explicit,但是,没有删除拷贝构造函数和赋值函数。

1)初始化

方法一:

shared_ptr<AA> p0(new AA("西施"));   // 分配内存并初始化。

方法二:推荐采用

shared_ptr<AA> p0 = make_shared<AA>("西施"); // C++11标准,效率更高。

方法三:

AA* p = new AA("西施");
shared_ptr<AA> p0(p);         // 用已存在的地址初始化。

方法四:

shared_ptr<AA> p0(new AA("西施")); 
shared_ptr<AA> p1(p0);         // 用已存在的shared_ptr初始化,计数加1。
shared_ptr<AA> p1=p0;         // 用已存在的shared_ptr初始化,计数加1。

2)使用方法

  • 智能指针重载了*和->操作符,可以像使用指针一样使用shared_ptr。

  • use_count()方法返回引用计数器的值。

  • unique()方法,如果use_count()为1,返回true,否则返回false。

  • shared_ptr支持赋值,左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1。

  • get()方法返回裸指针。

  • 不要用同一个裸指针初始化多个shared_ptr。即不要用方法三方式构造多个shared_ptr。

  • 不要用shared_ptr管理不是new分配的内存。

3)用于函数的参数

与unique_ptr的原理相同。

  • 传引用(不能传值,因为unique_ptr没有拷贝构造函数)。

  • 裸指针。

4)不支持指针的运算(+、-、++、--)

2、更多细节 面试就爱考点技巧之类的

2)用nullptr给shared_ptr赋值将把计数减1,如果计数为0,将释放对象,空的shared_ptr==nullptr。

4)std::move()可以转移对原始指针的控制权。还可以将unique_ptr转移成shared_ptr。

5)reset()改变与资源的关联关系。

pp.reset();     // 解除与资源的关系,资源的引用计数减1。
pp. reset(new AA("bbb"));  // 解除与资源的关系,资源的引用计数减1。关联新资源。

6)swap()交换两个shared_ptr的控制权。

void swap(shared_ptr<T> &_Right);

7)shared_ptr也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。

8)shared_ptr不是绝对安全,如果程序中调用exit()退出,全局的shared_ptr可以自动释放,但局部的shared_ptr无法释放。

9)shared_ptr提供了支持数组的具体化版本。

数组版本的shared_ptr,重载了操作符[],操作符[]返回的是引用,可以作为左值使用。

10)shared_ptr的线程安全性:

  • shared_ptr的引用计数本身是线程安全(引用计数是原子操作)。

  • 多个线程同时读同一个shared_ptr对象是线程安全的。

  • 如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。

  • 多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。

11)如果unique_ptr能解决问题,就不要用shared_ptr。unique_ptr的效率更高,占用的资源更少。

3、智能指针的删除器

智能指针构造函数第二个参数一般用默认的,但可以指定删除器。

在默认情况下,智能指针过期的时候,用delete原始指针;释放它管理的资源。

程序员可以自定义删除器,改变智能指针释放资源的行为。

删除器可以是全局函数、仿函数和Lambda表达式,形参为原始指针。

调用:

shared_ptr<AA> pa1(new AA("西施a"), deletefunc);  // 第二个参数一般都默认不写,但可以自定义删除器。

三、智能指针weak_ptr

1、shared_ptr存在的问题

shared_ptr内部维护了一个共享的引用计数器,多个shared_ptr可以指向同一个资源。

如果出现了循环引用的情况,引用计数永远无法归0,资源不会被释放。

2、weak_ptr是什么

weak_ptr 是为了配合shared_ptr而引入的,它指向一个由shared_ptr管理的资源但不影响资源的生命周期。也就是说,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。

不论是否有weak_ptr指向,如果最后一个指向资源的shared_ptr被销毁,资源就会被释放。

weak_ptr更像是shared_ptr的助手而不是智能指针。

3、如何使用weak_ptr

weak_ptr没有重载 ->和 *操作符,不能直接访问资源。

有以下成员函数:

operator=(); // 把shared_ptr或weak_ptr赋值给weak_ptr。
expired();   // 判断它指资源是否已过期(已经被销毁)。
lock();    // 返回shared_ptr,如果资源已过期,返回空的shared_ptr。
reset();    // 将当前weak_ptr指针置为空。
swap();    // 交换。

weak_ptr不控制对象的生命周期,但是,它知道对象是否还活着。

用lock()函数把它可以提升为shared_ptr,如果对象还活着,返回有效的shared_ptr,如果对象已经死了,提升会失败,返回一个空的shared_ptr。

提升的行为(lock())是线程安全的。

标签:AA,智能,new,cpp,shared,unique,ptr,指针
From: https://blog.csdn.net/weixin_56520780/article/details/145055849

相关文章

  • 计算机毕业设计Python深度学习游戏推荐系统 Django PySpark游戏可视化 游戏数据分析
    温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO......
  • 基于单片机的智能苗圃灌溉系统设计与实现:嵌入式程序设计
    基于单片机的智能苗圃灌溉系统设计与实现:嵌入式程序设计引言随着智能化农业的迅速发展,传统的农业灌溉模式逐渐被现代化智能灌溉系统取代。智能苗圃灌溉系统作为智能农业的重要组成部分,通过结合传感器技术、单片机控制技术以及数据通信技术,能够实现苗圃灌溉的自动化与智能......
  • 在Lazarus下的Free Pascal编程教程——打造有智能感知的用户设置操作界面
    0.前言我想通过编写一个完整的游戏程序方式引导读者体验程序设计的全过程。我将采用多种方式编写具有相同效果的应用程序,并通过不同方式形成的代码和实现方法的对比来理解程序开发更深层的知识。了解我编写教程的思路,请参阅体现我最初想法的那篇文章中的“1.编程计划”和“2.已经......
  • AI Agent(智能体)技术白皮书(Google,2024)中文+英文
    译者序本文翻译自2024年Google团队的一份 Agents白皮书,作者JuliaWiesinger,PatrickMarlow,VladimirVuskovic。Agent可以理解为是一个扩展了大模型出厂能力的应用程序。工具的使用,是人类区别于动物的标志——也是Agent区别于大模型的标志。水平及维护精力所......
  • 多智能体平台是一种专门设计用于开发、定制和部署AI Agents的平台或工具集
    多智能体平台是一种专门设计用于开发、定制和部署AIAgents的平台或工具集。以下是一些主流的多智能体平台,以及它们的优劣分析、生态情况、开源情况和是否支持离线部署与中文的相关信息:1.AutoGen介绍:由微软研发,专注于代码生成和软件开发,分为用户智能体和助手智能体,形成......
  • 使用云数据库RDS和低代码开发平台“魔笔”,高效构建门户网站,完成任务领智能台灯!
    传统建站涵盖需求分析、设计、开发、运维等环节。需分析软硬件需求并购买配置,设计网页布局,前后端编码调试,涉及业务逻辑、数据库及API。更新时需调整设计与代码。运营期需进行服务器维护、数据监控和安全检查,整个过程耗资费力。本方案将为您详细介绍如何使用云数据库RDS和低代码开......
  • 《从传统到智能:大模型交换机的变革之路》
    大模型交换机是一种专门为大规模人工智能模型提供网络和计算资源调度的硬件设备。以下是关于它的详细介绍:特点高带宽和低延迟:大模型的训练和推理通常需要处理大量的数据,高带宽可以确保数据在各个计算节点之间快速传输,低延迟则能减少数据传输过程中可能出现的瓶颈,提高训练和......
  • 医疗文档协作方式大革新:从手动到智能的蜕变
    随着医疗技术的发展,复杂病例越来越依赖多学科诊疗(MDT)的协作模式。然而,MDT的高效运行面临一个核心挑战:团队间的文档共享和沟通是否足够顺畅?以某国际医院为例,其MDT会诊流程中,GoogleDocs成为解决文档协作问题的关键工具:所有参与医生可在云端同步查看患者病例,添加标注,甚至实时讨论并......
  • 腾讯云AI代码助手编程挑战赛:基于腾讯混元实现智能问答与交互AI对话功能
    引言在当今人工智能迅猛发展的时代,智能问答与交互AI对话功能正逐渐渗透到各个领域,从日常生活中的语音助手到企业级的智能客服系统。腾讯云AI代码助手编程挑战赛正在火热进行中,腾讯云AI代码助手编程挑战赛作为“AI开发黑客松”系列活动的关键部分,其主题“用AI构造AI-打造......
  • 智能电网巡检与传感器数据自动分析:3大AI技术助力设备状态实时监测
    前言在智能电网的运行中,设备的实时监测和巡检报告的分析至关重要。定期的巡检报告和传感器数据是确保设备正常运行、预防故障发生的重要依据。然而,传统的人工分析方法不仅耗时,而且易出错。随着人工智能技术的发展,AI系统已能够高效、精准地从电力行业的巡检报告和传感器数据中提取......