C/C++指针智能指针二

C/C++指针&智能指针二

文章目录

  • C/C++指针&智能指针二
    • 1.智能指针简介
    • 2.独占智能指针unique_ptr
      • 1.基本概念
      • 2.使用方法
        • 1.初始化
      • 3.使用技巧
    • 3.共享智能指针
      • 1.基本概念
      • 2.使用方法
      • 3.注意事项
    • 4.智能指针删除器
      • 1.如何使用删除器
      • 2.应用场景
    • 5.弱智能指针weak_ptr
      • 1.基本概念
      • 2.使用方法
      • 3.注意事项

1.智能指针简介

C++智能指针是为了解决原始指针可能导致的内存泄漏、悬挂指针等问题而引入的。它们是C++标准库中模板类,自动管理指针所指向对象的生命周期,以RAII(Resource Acquisition Is Initialization)原则为基础,确保了对象在不再需要时能被正确地销毁和释放内存。C++11引入了三种主要的智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptr

  1. std::unique_ptr(独占智能指针):

    • std::unique_ptr 表示对一个对象的独占所有权。一个对象只能有一个 unique_ptr 拥有者,当这个 unique_ptr 离开作用域或者被显式重置时,它所指向的对象会被自动删除。
    • 它不支持拷贝构造函数和赋值操作符,但支持移动语义,可以通过移动构造函数或移动赋值将所有权转移给另一个 unique_ptr
  2. std::shared_ptr(共享拥有权智能指针):

    • std::shared_ptr 允许多个智能指针共享同一个对象的所有权。它内部维护了一个引用计数器,每当一个新的 shared_ptr 被创建指向同一个对象时,引用计数增加;当一个 shared_ptr 被销毁或被重置时,引用计数减少。当引用计数降至0时,对象被自动删除。
    • 由于其共享性质,shared_ptr 支持拷贝构造和赋值操作。
  3. std::weak_ptr(弱引用智能指针):

    • std::weak_ptr 是一种不增加引用计数的智能指针,它观测由某个 shared_ptr 管理的对象。主要用于解决循环引用问题,因为 weak_ptr 不影响对象的生命周期,即使持有该 weak_ptr 的所有对象都存在,也不会阻止被观测的对象被销毁。
    • 使用 weak_ptr 需要通过 lock() 成员函数转换为临时的 shared_ptr 才能访问对象,以检查对象是否还存在。

这些智能指针的使用简化了资源管理,减少了内存泄漏的风险,并且提高了代码的安全性和可维护性。在现代C++编程中,它们被广泛推荐用于原生指针的替代品。

2.独占智能指针unique_ptr

1.基本概念

std::unique_ptr 是C++中的一种独占式智能指针,它体现了唯一拥有权的概念。这意味着在同一时间内,只能有一个 unique_ptr 实例指向某个动态分配的内存区域。这种设计旨在防止多个指针同时修改或释放同一块内存,从而避免了资源竞争和潜在的内存泄漏问题。

  • 所有权转移unique_ptr 支持移动语义(通过移动构造函数和移动赋值运算符),允许将所有权从一个 unique_ptr 转移到另一个 unique_ptr 上,但不支持普通的拷贝(没有拷贝构造函数和拷贝赋值运算符)。

  • 自动管理内存:当 unique_ptr 离开其作用域时,它会自动删除所管理的动态分配的对象,无须手动调用 delete

  • 初始化与重置

    • 初始化:可以通过直接初始化或赋值初始化创建 unique_ptr,并立即让它管理一个新分配的内存。
    • 重置:可以使用 reset() 方法来让 unique_ptr 管理一个新的对象或释放当前管理的对象。
  • 裸指针访问:提供了 get() 方法来获取底层的原始指针,便于与不接受智能指针的旧代码交互。

示例

#include <iostream>
#include <memory>int main() {// 创建并初始化unique_ptrstd::unique_ptr<int> ptr(new int(42));// 访问unique_ptr管理的对象std::cout << "Value: " << *ptr << std::endl;// 通过移动构造函数转移所有权std::unique_ptr<int> anotherPtr(std::move(ptr));// 此时ptr不再拥有任何对象if (!ptr) {std::cout << "ptr is now empty." << std::endl;}// 可以继续使用anotherPtr*anotherPtr = 100;std::cout << "New value: " << *anotherPtr << std::endl;return 0;
}

在这个例子中,unique_ptr ptr 初始化后管理一个整数值为42的动态分配的内存。然后,通过移动构造函数,这个管理权被转移到了 anotherPtr,之后 ptr 就不再拥有任何对象。最后,我们修改了 anotherPtr 指向的值并打印出来,展示了所有权转移和自动内存管理的特性。

为什么要使用智能指针呢?

看下面这个例子:

//
// Created by 86189 on 2024/7/14.
//
#include <iostream>using namespace std;class student{
public:string name;student(){cout << "nothing func" << endl;}student(string name){this->name = name;cout << "have func" << endl;}~student(){cout << "delete func" << endl;}
};int main(){student *p = new student;p->name = "jack";return 0;
}

运行程序发现只用类的无参构造函数被调用 ,而析构函数并没有被调用,也就是说我们new出来的空间并没用被释放,当我们在程序的结尾添加上delete p时,析构函数才被调用。而当我们使用智能指针时,如下:

//
// Created by 86189 on 2024/7/14.
//
#include <iostream>
#include <utility>
#include <memory>using namespace std;class student{
public:string name;student(){cout << "nothing func" << endl;}explicit student(string name){this->name = std::move(name);cout << "have func" << endl;}~student(){cout << "delete func" << endl;}
};int main(){auto *p = new student;
//    p->name = "jack";
//    delete p;unique_ptr<student>ptr(p);ptr->name = "jack";return 0;
}

并不需要我们主动释放内存,编译器会自动帮我们释放。

2.使用方法

1.初始化

方法一:

unique_str<typet>ptr(new typet(value)); //分配内存并初始化
例如:
unique_str<student>ptr(new student("jack"));

方法二:

unique_str<typet>ptr = make_ptr<typet>(value)
例如:
unique_str<student>ptr = make_ptr<student>("jack");

方法三:

auto *p = new type;
unique_str<typet>ptr(p); //使用已知的地址初始化
例如:
auto *p = new student;
unique_ptr<student>ptr(p);
ptr.get(); // 返回原始指针p

注意:

不能普通指针赋值给智能指针

不能使用智能指针的拷贝构造函数

不能使用=对智能指针进行赋值

智能指针重载*操作符,可以使用解引用操作

在智能指针作为函数参数进行传递时,不能使用值传递,可使用传递引用或者地址 void func(unique_ptr<int> &ptr)

智能指针不支持指针的运算

3.使用技巧

  1. 利用构造函数初始化:最好在定义 unique_ptr 时就直接初始化它,这样可以确保资源从一开始就得到妥善管理。避免先声明后初始化,这可能会导致资源泄露。

  2. 使用make_unique:自C++14起,推荐使用 std::make_unique 函数来创建 unique_ptr,这样可以更安全地处理异常情况,并且代码更简洁易读。

    auto ptr = std::make_unique<MyClass>(args...);
    
  3. 利用移动语义:通过移动构造函数和移动赋值操作符,可以在函数之间高效地传递所有权。这在需要将资源所有权从一个作用域转移到另一个作用域时特别有用。

    void foo(std::unique_ptr<MyClass> ptr) {// ...
    }int main() {auto ptr = std::make_unique<MyClass>();foo(std::move(ptr)); // 移动所有权// 此处ptr不再拥有对象
    }
    
  4. 避免裸指针操作:尽量使用 unique_ptr 管理对象,避免直接使用裸指针,以减少内存泄漏的风险。

  5. 使用get()与释放所有权:在需要将 unique_ptr 管理的对象传递给API,该API期望裸指针时,可以使用 get() 方法获取原始指针。但是,务必确保不会因此导致对象的生命周期管理出现问题。

  6. 利用reset()进行重置:当需要改变 unique_ptr 管理的对象时,可以使用 reset() 方法。如果之前管理了对象,它会被正确销毁。

    ptr.reset(new MyClass()); // 重新分配内存
    ptr.reset(); // 释放当前管理的内存,使unique_ptr变为nullptr
    
  7. 避免循环依赖:由于 unique_ptr 不支持共享所有权,所以在设计类的成员时,应避免通过 unique_ptr 形成循环依赖,这可能导致资源无法释放。

  8. 模板与多态unique_ptr 可以用于持有基类指针,实现多态性,并且能够安全地管理派生类对象的生命周期。这在设计灵活且类型安全的系统时非常有用。

  9. 单元测试:在编写使用 unique_ptr 的类或函数的单元测试时,可以先在栈上创建对象进行测试,以确保逻辑正确,然后再使用智能指针进行集成测试。

  10. 文档化所有权转移:在代码中明确注释所有权的转移,特别是当通过函数参数或返回值进行传递时,有助于提高代码的可读性和维护性。

3.共享智能指针

std::shared_ptr 是C++中一种基于引用计数的智能指针,它允许多个 shared_ptr 实例共享同一个对象的所有权。当最后一个指向该对象的 shared_ptr 销毁时,该对象会被自动删除,从而有效防止内存泄漏。

1.基本概念

  • 引用计数shared_ptr 内部维护一个引用计数器,记录有多少个 shared_ptr 指向同一个对象。每当创建一个新的 shared_ptr 指向该对象时,引用计数增加;当一个 shared_ptr 被销毁或被重置指向其他对象时,引用计数减少。当引用计数降至0,对象将被自动析构。

  • 共享所有权:多个 shared_ptr 可以同时拥有同一个对象的所有权,任何一个 shared_ptr 的析构都会减少引用计数,当引用计数为0时,对象被释放。

  • 线程安全性shared_ptr 的引用计数操作通常是线程安全的(原子操作),这意味着在多线程环境下增加或减少引用计数不会引发数据竞争。然而,如果多个线程同时修改 shared_ptr (例如通过赋值或重置),则需要外部同步机制来保证线程安全。

2.使用方法

  1. 创建与初始化

    • 直接初始化:std::shared_ptr<int> sptr(new int(42));
    • 使用 make_shared:推荐做法,因为更高效且能确保内存分配与构造函数调用的原子性。
      std::shared_ptr<int> sptr = std::make_shared<int>(42);
      
  2. 拷贝与赋值shared_ptr 支持拷贝构造函数和赋值运算符,拷贝后两个 shared_ptr 实例将共享同一个对象的所有权。

    std::shared_ptr<int> sptr2 = sptr; // sptr和sptr2现在共享所有权
    
  3. 访问原始指针:使用 get() 方法获取原始指针。

    int* rawPtr = sptr.get();
    
  4. 检查是否为空:使用 bool 类型转换或 expired() 方法检查 shared_ptr 是否为空或所指向的对象是否已被释放。

    if (sptr) { /* 非空 */ }
    
  5. 重置与交换

    • reset() 方法可以用来释放当前指向的对象(如果引用计数允许的话),并可选择指向新的对象。
    • swap() 方法可以交换两个 shared_ptr 的管理对象。
  6. 自定义删除器:在创建 shared_ptr 时,可以提供一个自定义删除器,用于在对象被删除时执行特定的操作。

3.注意事项

  • 循环引用shared_ptr 最大的风险在于可能引起循环引用,导致对象无法释放。在涉及循环引用的场景中,应该考虑使用 std::weak_ptr 来断开引用链。

  • 性能考量:由于引用计数的维护,shared_ptr 相比于原始指针或 unique_ptr 会有一定的性能开销,尤其是在频繁拷贝或分配大量小对象时。

  • 资源管理:虽然 shared_ptr 自动管理内存,但正确使用它仍需开发者对对象生命周期有清晰的认识,避免不必要的资源占用。

4.智能指针删除器

智能指针删除器是在C++中使用智能指针(如std::unique_ptrstd::shared_ptr)时,提供的一种机制,用于自定义管理所指向对象生命周期结束时的清理行为。默认情况下,智能指针使用delete操作符来释放内存,但通过指定删除器,你可以改变这一行为,以适应更复杂的资源管理需求,比如管理通过malloc分配的内存、关闭文件描述符、释放自定义资源等。

1.如何使用删除器

  1. 对于std::unique_ptr

    void customDeleter(int* ptr) {// 自定义释放内存或其他操作free(ptr); // 或其他清理操作
    }std::unique_ptr<int, decltype(&customDeleter)> uniquePtr(new int(42), customDeleter);
    

    在这里,decltype(&customDeleter) 是删除器类型的显式指定,customDeleter 是实际的删除器函数。

  2. 对于std::shared_ptr

    std::shared_ptr<int> sharedPtr(new int(42), customDeleter);
    

    同样,customDeleter 是自定义的删除器函数,但在这里类型推导会自动确定删除器的类型,所以不需要显式指定。

  3. 使用lambda作为删除器

    auto deleter = [](int* ptr) {// 自定义释放逻辑delete ptr;
    };
    std::unique_ptr<int, decltype(deleter)> uniquePtrWithLambda(new int(42), deleter);
    
  4. 使用std::make_shared与删除器
    对于std::shared_ptr,虽然std::make_shared不直接支持传递删除器,但可以通过创建一个没有删除器的shared_ptr,然后使用std::shared_ptr的拷贝构造函数和自定义删除器来创建一个新的shared_ptr实例。

2.应用场景

  • 当对象不是通过new分配的,而是通过其他方式(如malloc)分配时,需要自定义删除器来释放资源。
  • 管理非内存资源,如文件句柄、网络连接等,需要在对象生命周期结束时执行特定的清理操作。
  • 在需要执行额外清理工作或者遵循特定清理协议的情况下。

通过使用自定义删除器,智能指针变得更加灵活,能够适应更广泛的资源管理需求,增强了C++代码的安全性和可维护性。

5.弱智能指针weak_ptr

std::weak_ptr 是C++智能指针家族中的一个成员,专门设计用于解决由std::shared_ptr引起的循环引用问题。它是对shared_ptr所管理对象的一种非拥有(或者说不增加引用计数)的引用方式,主要用于监控对象的存在性而不影响对象的生命周期管理。

1.基本概念

  • 非拥有性weak_ptr 不增加它所指向的shared_ptr管理对象的引用计数。这意味着,即使存在多个weak_ptr指向同一对象,该对象也仅当最后一个关联的shared_ptr销毁时才会被释放。

  • 监测作用:它主要用于在不延长对象生命周期的前提下,安全地访问可能由shared_ptr管理的对象。这对于避免因循环引用导致的内存泄漏尤为重要。

  • 生存确认:由于weak_ptr不控制对象生命周期,使用前必须通过lock()方法检查对象是否仍然存在,lock()会返回一个指向相同对象的临时shared_ptr,若对象已被销毁,则返回一个空的shared_ptr

2.使用方法

  1. shared_ptr创建

    std::shared_ptr<int> sptr = std::make_shared<int>(42);
    std::weak_ptr<int> wptr(sptr); // 从shared_ptr构造weak_ptr
    
  2. 检查对象有效性

    if(auto sp = wptr.lock()) { // 尝试锁定并检查对象是否存在std::cout << "*wptr: " << *sp << std::endl; // 安全访问对象
    } else {std::cout << "Object no longer exists." << std::endl;
    }
    
  3. 访问对象:通过lock()方法获得的临时shared_ptr来访问对象,确保了对象存在的同时,也遵循了RAII原则。

  4. 循环引用解决方案:在设计含有相互引用的类时,可以使用weak_ptr替代一个方向上的shared_ptr引用,以打破循环,确保没有不必要的生命周期扩展。

3.注意事项

  • 非直接访问weak_ptr本身不支持类似*->这样的直接访问操作,必须先转换为shared_ptr

  • 生命周期管理:尽管weak_ptr不直接影响对象生命周期,但应谨慎使用,避免不经意间延长了对象的生命周期,尤其是当使用lock()后未及时释放对应的shared_ptr时。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/871932.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

雷赛运动控制卡编程(2)限位设定与回原方式、脉冲模式设置

一、限位开关及急停开关的设置 限位开关的设置 //设置限位开关// dmc_set_el_modeushort _cardNo0;ushort _axisNo 0;ushort _enable 1; // 正负限位使能 ushort _elLogic 0; // 正负限位 低电平 有效ushort _model 0;// 正负限位停止方式为立即停止LTDMC.dmc_set_el_m…

构建实用的NLP应用程序:重塑人类与计算机的协同工作方式

文章目录 一、NLP技术的核心价值二、构建实用NLP应用程序的关键步骤三、NLP应用程序在协同工作中的创新应用《赠ChatGPT中文范例的自然语言处理入门书》编辑推荐内容简介作者简介精彩书评目录前言/序言获取方式 在数字化时代&#xff0c;自然语言处理&#xff08;NLP&#xff0…

手机数据恢复:适用于 Android 的 4 大数据恢复应用程序

没有人希望丢失设备上的重要数据。如果发生这种情况&#xff0c;请不要惊慌。以下是可帮助您恢复丢失或删除的数据的 Android 数据恢复应用程序列表。 有多种方法可以恢复已删除或丢失的 Android 数据&#xff0c;最简单、最快捷的方法是使用第三方恢复应用程序。这些应用程序会…

Transformer模型:Postion Embedding实现

前言 这是对上一篇WordEmbedding的续篇PositionEmbedding。 视频链接&#xff1a;19、Transformer模型Encoder原理精讲及其PyTorch逐行实现_哔哩哔哩_bilibili 上一篇链接&#xff1a;Transformer模型&#xff1a;WordEmbedding实现-CSDN博客 正文 先回顾一下原论文中对Posit…

[Windows] 号称最快免费小巧的远程桌面 AnyDesk v8.0.11单文件版

描述 对于经常在互联网上进行操作的学生&#xff0c;白领等&#xff01; 一款好用的软件总是能得心应手&#xff0c;事半功倍。 今天给大家带了一款高科技软件 虽然 QQ 拥有远程协助功能&#xff0c;但很多时候连接并不够流畅&#xff0c;而且被控电脑那方也必须要有人操作才行…

电脑关机被阻止

1. winR输入regedit进入注册表 2. 选择HKEY_USERS-》.DEFAULT-》Control Panel-》Desktop 3. 右键DeskTop新建字符串值&#xff0c;命名为AutoEndTasks&#xff0c;数值设置为1

C++中链表的底层迭代器实现

大家都知道在C的学习中迭代器是必不可少的&#xff0c;今天我们学习的是C中的链表的底层迭代器的实现&#xff0c;首先我们应该先知道链表的底层迭代器和顺序表的底层迭代器在实现上有什么区别&#xff0c;为什么顺序表的底层迭代器更加容易实现&#xff0c;而链表的底层迭代器…

不会编程怎么办?量化交易不会编程可以使用吗?

量化交易使用计算机模型程序代替人工进行交易&#xff0c;一般需要投资者自己编写程序建模&#xff0c;然后回测无误之后再进行实盘交易&#xff0c;那么不会编程的投资者能使用量化软件进行量化交易吗&#xff1f; 不会编程使用量化软件有两种方法 一种是请人代写代码&#x…

Java软件设计模式-单例设计模式

目录 1.软件设计模式的概念 2.设计模式分类 2.1 创建型模式 2.2 结构型模式 2.3 行为型模式 3.单例设计模式 3.1 单例模式的结构 3.2 单例模式的实现 3.2.1 饿汉式-方式1&#xff08;静态变量方式&#xff09; 3.2.2 懒汉式-方式1&#xff08;线程不安全&#xff09; 3.…

职场新人感受

互联网职场感受 阶段介绍 24届6月底毕业生&#xff0c;之前从未实习过。 岗位是后端开发&#xff08;JAVA&#xff09;&#xff0c;目前已经上班三周&#xff08;前两周看文档和做了半个简单需求&#xff0c;第三周脱产新人培训&#xff09;。 职场体验 职场和想象中的工作…

c++ 网络编程udp协议 poco模块

官网资料(需要梯子)&#xff1a;https://pocoproject.org/slides/200-Network.pdf 1、poco是在原生socket之上的封装&#xff0c;底层还是socket&#xff0c;性能低于socket&#xff0c;安全性和实用性高于socket&#xff0c;即使用简便&#xff0c;接口简单 2、udp协议是&…

办公灯多普勒雷达模组感应开关,飞睿智能24G毫米波雷达超低功耗uA级,节能LED灯新搭档

在科技日新月异的今天&#xff0c;节能、环保已经成为我们生活和工作中不可或缺的一部分。作为新时代的办公人&#xff0c;我们不仅要追求高效的工作方式&#xff0c;更要关注我们所使用的设备是否足够环保、节能。今天&#xff0c;我们就来聊聊一个令人兴奋的创新——飞睿智能…

COMX-P2020、COMX-P1022 vxWorks系统开发主机

提供vxworks6.9开发环境和BSP源码&#xff0c;具有千兆以太网&#xff0c;调试串口&#xff0c;4个PCIe插槽&#xff0c;支持PCIe 1.0a和msi中断&#xff0c;底板板载一块Xilinx CPLD XC95144&#xff0c;提供ISE14.7安装包和verilog源码。可定制开发基于PCIe接口和fpga的拓展接…

多语言环境大师:在PyCharm中管理多个Python解释器

多语言环境大师&#xff1a;在PyCharm中管理多个Python解释器 PyCharm作为业界领先的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;支持多种Python解释器的配置和管理&#xff0c;使得开发者可以针对不同项目使用不同的Python环境。本文将详细介绍如何在PyCharm…

如何30分钟下载完368G的Android系统源码?

如何30分钟下载完368G的Android系统源码&#xff1f; Android系统开发的一个痛点问题就是Android系统源码庞大&#xff0c;小则100G,大则&#xff0c;三四百G。如标题所言&#xff0c;本文介绍通过局域网高速网速下载源码的方法。 制作源码mirror 从源码git服务器A&#xff0c…

推荐系统:从协同过滤到深度学习

目录 一、协同过滤&#xff08;Collaborative Filtering, CF&#xff09;1. 基于用户的协同过滤2. 基于物品的协同过滤 二、深度学习在推荐系统中的应用1. 深度学习模型的优势2. 深度学习在推荐系统中的应用实例 三、总结与展望 推荐系统是现代信息处理和传播中不可或缺的技术&…

【话题】破茧而出:打破AI“信息茧房”,捍卫信息自由与多样性

目录 AI发展下的伦理挑战&#xff0c;应当如何应对&#xff1f; 方向一&#xff1a;构建可靠的AI隐私保护机制 方向二&#xff1a;确保AI算法的公正性和透明度 方向三&#xff1a;管控深度伪造技术 AI发展下的伦理挑战&#xff0c;应当如何应对&#xff1f; 在人工智能&…

Tita的OKR:高端制造行业的OKR案例

高端设备制造行业的发展趋势&#xff1a; 产业规模持续扩大&#xff1a;在高技术制造业方面&#xff0c;航空、航天器及设备制造业、电子工业专用设备制造等保持较快增长。新能源汽车保持产销双增&#xff0c;新材料新产品生产也高速增长。 标志性装备不断突破&#xff1a;例如…

数据结构第27节 优先队列

优先队列&#xff08;Priority Queue&#xff09;是在计算机科学中一种非常有用的抽象数据类型&#xff0c;它与标准队列的主要区别在于元素的出队顺序不是先进先出&#xff08;FIFO&#xff09;&#xff0c;而是基于每个元素的优先级。具有较高优先级的元素会比低优先级的元素…

论文写作经验-摘要1

小王搬运工 时序课堂 2024年07月15日 13:10 新疆 本人菜鸡一名&#xff0c;最近几篇论文实验跑的比较顺利&#xff0c;结果也很不错&#xff0c;奈何于自己写作能力巨差&#xff0c;导致文章屡屡被拒。当前正在跟一位非常牛的老师学习写作技巧&#xff0c;我将一些心得体会和技…