c++ 学习系列 -- 智能指针

news2025/1/12 5:55:49

一   为什么引入智能指针?解决了什么问题?

C++ 程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。但使用普通指针,容易造成内存泄露(忘记释放)、二次释放、程序发生异常时内存泄露等问题等。

另外,使用普通指针容易产生 野指针、悬空指针 等问题。

所以 C++11 就引入了智能指针来管理内存。

二  常用的智能指针与区别

常用智能指针有  shared_ptrunique_ptr weak_ptr 

  • unique_ptr: 独占式指针,同一时刻只允许有一个 unique_ptr 指针指向一个对象
  • shared_ptr: 共享式指针,同一时刻允许多个 shared_ptr 指针指向同一个对象。
    •  缺点:出现相互引用时,容易导致死锁或者内存无法释放内存的问题
  • weak_ptr: 为了解决 shared_ptr 相互引用可能导致的死锁或无法释放内存的问题而引入,通常与shared_ptr 配合使用。但是由于缺少引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。 C++11智能指针(weak_ptr) - 简书 (jianshu.com)

三  常用智能指针使用例子

   1  unique_ptr 例子

        unique_ptr 是一个独享所有权的智能指针

#include<memory>
#include<string>
#include<iostream>

class Person
{
public:
    Person(std::string name):m_name(name)
    {
        std::cout << "Person constructor name: " << m_name << std::endl;
    }

    ~Person()
    {
        std::cout << "Person destructor name: " << m_name << std::endl;
    }

private:
    std::string m_name;
};

#include<memory>



void testUniquePtr()
{
    std::unique_ptr<Person>  p1_ptr(new Person("P1 ---"));
    std::unique_ptr<Person>  p2_ptr = std::move(p1_ptr);

   // std::unique_ptr<Person>  p3_ptr = p2_ptr; // 编译不过
   // std::unique_ptr<Person>  p4_ptr(p2_ptr); // 编译不过

}


int main(int argc, char *argv[])
{
    testUniquePtr();
    return 0;
}

   输出

 

2   shared_ptr 例子

#include<memory>
#include<iostream>
#include<string>

using namespace std;

void testSharedPtr()
{
        shared_ptr<string> pa(new string("PAAAA"));
        shared_ptr<string> pb(new string("PBBBB"));
        cout << "*pa " << *pa << endl;//CHN
        cout << "pa.use_count " << pa.use_count() << endl;//1
        cout << "*pb " << *pb << endl;//USA
        cout << "pb.use_count " << pb.use_count() << endl;//1

        pa = pb;
        cout << *pa << endl;//USA
        cout << "pa.use_count " << pa.use_count() << endl;//2:pa和pb指向同一个资源USA了,该资源的计数为2,所以pb、pb都输出2
        cout << "pb.use_count " << pb.use_count() << endl;//2

        pa.reset();
        pb.reset();
        cout << "pa.use_count " << pa.use_count() << endl;//0
        cout << "pb.use_count " << pb.use_count() << endl;//0
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    testSharedPtr();
    return a.exec();
}

   3  weak_ptr 例子

       3.1  shared_ptr 相互引用的问题

// a.h
#include<memory>

class B;

class A
{
public:
    A()
   {
       std::cout << "A constructor ---" << std::endl;
   }

    ~A()
   {
    std::cout << "A destructor ---" << std::endl;
   }

public:
    std::shared_ptr<B> m_b_ptr;
};

// b.h
#include<memory>

class A;

class B
{
public:
    B()
    {
       std::cout << "A constructor ---" << std::endl;
   }
    ~B()
    {
       std::cout << "A destructor ---" << std::endl;
   }

public:
    std::shared_ptr<A> m_a_ptr;
};


// main.cpp
void testSharedPtr()
{
    std::shared_ptr<A> pa(new A);
    cout << "pa.use_count " << pa.use_count() << endl;//1

    std::shared_ptr<B> pb(new B);
    cout << "pb.use_count " << pb.use_count() << endl;//1

    pa->m_b_ptr = pb;
    cout << "pb.use_count " << pb.use_count() << endl;//2
    cout << "pa.use_count " << pa.use_count() << endl;//1
    pb->m_a_ptr = pa;
    //由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1
    cout << "pb.use_count " << pb.use_count() << endl;//2
    cout << "pa.use_count " << pa.use_count() << endl;//2

}

int main(int argc, char *argv[])
{
    testSharedPtr();

    return 0;
}


  输出 :     

通过输出可以看到未执行析构函数,存在内存泄漏。

引用计数分别增加到了 2 ,不为 0 就意味着无法释放内存。

3.2  weak_ptr 与 share_ptr 使用 

// a.h
#include<memory>

class B;

class A
{
public:
    A()
   {
       std::cout << "A constructor ---" << std::endl;
   }

    ~A()
   {
    std::cout << "A destructor ---" << std::endl;
   }

public:
    std::weak_ptr<B> m_b_ptr;
};

// b.h
#include<memory>

class A;

class B
{
public:
    B()
    {
       std::cout << "A constructor ---" << std::endl;
   }
    ~B()
    {
       std::cout << "A destructor ---" << std::endl;
   }

public:
    std::shared_ptr<A> m_a_ptr;
};


// main.cpp
void testWeakPtr()
{
 std::shared_ptr<A> pa(new A);
    cout << "pa.use_count " << pa.use_count() << endl;//1

    std::shared_ptr<B> pb(new B);
    cout << "pb.use_count " << pb.use_count() << endl;//1

    pa->m_b_ptr = pb;
    cout << "pb.use_count " << pb.use_count() << endl;//1
    cout << "pa.use_count " << pa.use_count() << endl;//2
    pb->m_a_ptr = pa;
    
    cout << "pb.use_count " << pb.use_count() << endl;//1  由于 weak_ptr 是弱引用,不会增加引用计数
    cout << "pa.use_count " << pa.use_count() << endl;//2  由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1


}

int main(int argc, char *argv[])
{
    testWeakPtr();

    return 0;
}


输出:

 通过输出可以看到执行析构函数,不存在内存泄漏。

//  资源B的引用计数一直就只有1,当pb析构时,B的计数减一,变为0,B得到释放,
//  B释放的同时也会使A的计数减一,同时pa自己析构时也会使资源A的计数减一,那么A的计数为0,A得到释放。

四  智能指针的原理与简单实现

智能指针实际运用的就是c++ 中的 RAII 技术,详情见  C++ 学习系列 二 -- RAII 机制_在河之洲木水的博客-CSDN博客

1. unique_ptr 

 因为是独占型指针,所以需要禁止拷贝构造函数与赋值函数

// my_nuique_ptr.h
template<typename T>
class my_unique_ptr
{
public:
    my_unique_ptr(T* ptr = nullptr);
    ~my_unique_ptr();
    my_unique_ptr(my_unique_ptr&& other_ptr); // c++ 中声明移动构造函数后,则自动禁用拷贝构造函数
    my_unique_ptr& operator=(my_unique_ptr&& other_ptr); // c++ 中声明移动赋值函数后,则自动禁用拷贝赋值函数

    T& operator*() const; // 指针的基本操作,取值
    T* operator->() const;
    operator bool() const; // 提供一个本类型到bool的隐式转换,不允许使用参数

private:
    T* m_ptr;
};

template<typename T>
my_unique_ptr<T>::my_unique_ptr(T* ptr):m_ptr(ptr)
{

}

template<typename T>
my_unique_ptr<T>::~my_unique_ptr()
{
    delete m_ptr;
}

template<typename T>
my_unique_ptr<T>::my_unique_ptr(my_unique_ptr&& other_ptr)
{
    this->m_ptr = other_ptr.m_ptr;
    other_ptr.m_ptr = nullptr;
}

template<typename T>
my_unique_ptr<T>&
my_unique_ptr<T>::operator=(my_unique_ptr&& other_ptr)
{
    this->m_ptr = other_ptr.m_ptr;
    other_ptr.m_ptr = nullptr;

    return this;
}

template<typename T>
T&   my_unique_ptr<T>::operator*() const
{
    return *m_ptr;
}

template<typename T>
T* my_unique_ptr<T>::operator->() const
{
    return m_ptr;
}

template<typename T>
my_unique_ptr<T>::operator bool() const
{
    return m_ptr;
}



// main.cpp

#include<iostream>
#include"my_unique_ptr.h"

void testFunc2()
{
    my_unique_ptr<Person> my_ptr(new Person("p1 ------"));
    std::cout << "person name1: "<<my_ptr->getName() << std::endl;
    my_unique_ptr<Person> my_ptr2(std::move(my_ptr));
    std::cout << "person name2: "<<my_ptr2->getName() << std::endl;

     //my_unique_ptr<Person> my_ptr3 = my_ptr; // 编译失败
    // my_unique_ptr<Person> my_ptr4(my_ptr); // 编译失败

}


int main()
{
   testFunc2();

   return 0;
}

输出:

 

2. shared_ptr

3. weak_ptr

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

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

相关文章

Springboot整合RabbitMq,详细步骤

Springboot整合RabbitMq&#xff0c;详细步骤 1 添加springboot-starter依赖2 添加连接配置3 在启动类上添加开启注解EnableRabbit4 创建RabbitMq的配置类&#xff0c;用于创建交换机&#xff0c;队列&#xff0c;绑定关系等基础信息。5 生产者推送消息6 消费者接收消息7 生产者…

闭环控制方法及其应用:优缺点、场景和未来发展

闭环控制是一种基本的控制方法&#xff0c;它通过对系统输出与期望值之间的误差进行反馈&#xff0c;从而调整系统输入&#xff0c;使系统输出更加接近期望值。闭环控制的主要目标是提高系统的稳定性、精确性和鲁棒性。在实际应用中&#xff0c;闭环控制有多种方法&#xff0c;…

开源代码分享(13)—整合本地电力市场与级联批发市场的投标策略(附matlab代码)

1.引言 1.1摘要 本地电力市场是在分配层面促进可再生能源的效率和使用的一种有前景的理念。然而&#xff0c;作为一个新概念&#xff0c;如何设计和将这些本地市场整合到现有市场结构中&#xff0c;并从中获得最大利润仍然不清楚。在本文中&#xff0c;我们提出了一个本地市场…

linux添加磁盘

一、linux虚拟机添加一块新的硬盘 四步&#xff1a; &#xff08;1&#xff09; &#xff08;2&#xff09;为硬盘进行分区 &#xff08;3&#xff09;初始化硬盘分区 &#xff08;4&#xff09;挂载 在虚拟机上添加一块硬盘 (1)、 虚拟机添加一块新的硬盘作为数据盘 (2) ls…

Idea Live Template 功能总结

文章目录 Java自带的template属性模板psf——public static finalpsfi——public static final intpsfi——public static final StringSt——String 方法模板psvm——main方法sout——打印语句iter——for迭代循环fori——for循环 代码块模板if-e —— if elseelse-if 自定义自…

中国首款量子计算机操作系统本源司南 PilotOS正式上线

中国安徽省量子计算工程研究中心近日宣布&#xff0c;中国国产量子计算机操作系统本源司南 PilotOS 客户端正式上线。 如果把量子芯片比喻成人的“心脏”&#xff0c;那么量子计算机操作系统就相当于人的“大脑”&#xff0c;量子计算应用软件则是人的“四肢”。 据安徽省量子…

Linux 终端命令之文件浏览(1) cat

Linux 文件浏览命令 cat, more, less, head, tail&#xff0c;此五个文件浏览类的命令皆为外部命令。 hannHannYang:~$ which cat /usr/bin/cat hannHannYang:~$ which more /usr/bin/more hannHannYang:~$ which less /usr/bin/less hannHannYang:~$ which head /usr/bin/he…

论文总结《Towards Evaluating the Robustness of Neural Networks(CW)》

原文链接 C&W 这篇论文更像是在讲一个优化问题&#xff0c;后面讲述如何针对生成对抗样本的不可解问题近似为一个可解的问题&#xff0c;很有启发。本文后面将总结论文各个部分的内容。 Motivation 文章提出了一个通用的设计生成对抗样本的方法&#xff0c;根据该论文提…

YAPi在线接口文档简单案例(结合Vue前端Demo)

在前后端分离开发中&#xff0c;我们都是基于文档进行开发&#xff0c;那前端人员有时候无法马上拿到后端的数据&#xff0c;该怎么办&#xff1f;我们一般采用mock模拟伪造数据直接进行测试&#xff0c;本篇文章主要介绍YApi在线接口文档的简单使用&#xff0c;并结合Vue的小d…

【C++学习】STL容器——stack和queue

目录 一、stack的介绍和使用 1.1 stack的介绍 1.2 stack的使用 1.3 stack的模拟实现 二、queue的介绍和使用 2.1 queue的介绍 2.2 queue的使用 2.3 queue的模拟实现 三、priority_queue的介绍和使用 3.1 priority_queue的介绍和使用 3.2 priority_queue的使用 3.4 p…

【Powershell 】(Windows下)常用命令 | 命令别名 | 运行Windows命令行工具 | 运行用户程序(vim、gcc、gdb)

微软官方Powershell文档&#xff1a;https://learn.microsoft.com/zh-cn/powershell/ 命令详细说明&#xff0c;在PDF的最后面&#xff1a; 一、Powershell及命令简介1.1 命令格式1.2 命令的别名 二、cmdlet别名三、cmdlet分类介绍3.1 基础命令1. Get-Command2. Get-Help3. S…

[HDLBIts] Exams/m2014 q4j

Implement the following circuit: ("FA" is a full adder) module top_module (input [3:0] x,input [3:0] y, output [4:0] sum);assign sumxy; endmodule

C数据结构与算法——无向图(邻接矩阵) 应用

实验任务 (1) 掌握图的邻接矩阵存储及基本算法&#xff1b; (2) 掌握该存储方式下的DFS和BFS算法。 实验内容 实现图的邻接矩阵存储结构实现基于邻接矩阵的相关算法及遍历算法 实验源码 #include <malloc.h> #include <stdio.h>#define MAXSIZE 1000 #define …

SpringBoot07——VueX

共享组件之间的数据&#xff0c;集中管理 这一部分某人要打ow我就跳过没看了&#xff0c;哼&#xff0c;都怪某人

【机器学习4】构建良好的训练数据集——数据预处理(一)处理缺失值及异常值

数据预处理 &#x1f4ab;数据预处理的重要性&#x1f4ab;处理缺失值⭐️识别表格中的数据⭐️计算每列缺失值的数量⭐️删除含有缺失值的样本或特征⭐️填充缺失值 &#x1f4ab;处理异常值⭐️异常值的鉴别⭐️异常值的处理 &#x1f4ab;将数据集划分为训练数据集和测试数据…

华为网络篇 RIP的Slient-Interface-26

难度1复杂度 1 目录 一、实验原理 二、实验拓扑 三、实验步骤 四、实验过程 总结 一、实验原理 在默认情况下&#xff0c;RIP会在所有的接口泛洪路由更新信息&#xff08;整个路由表&#xff09;&#xff0c;这里有一个问题&#xff0c;当RIP路由器连接的是一个末端网络时…

基层社会治理平台建设方案[113页PPT]

导读&#xff1a;原文《基层社会治理平台建设方案[113页PPT]》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 完整版领取方式 完整版领取方式&#xff1a; 如需获取完…

Python(八十二)字符串的常用操作——替换与合并

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

FreeRTOS(二值信号量)

资料来源于硬件家园&#xff1a;资料汇总 - FreeRTOS实时操作系统课程(多任务管理) 目录 一、信号量的概念 1、信号量的基本概念 2、信号量的分类 二、二值信号量的定义与应用 1、二值信号量的定义 2、二值信号量的应用 三、二值信号量的运作机制 1、FreeRTOS任务间二值…

应用冷启bindservice耗时

背景&#xff1a;sdk初始化的时候耗时过长&#xff0c;而sdk,init方法中只有一个bindservice及一些变量的初始化&#xff0c;却好事100ms 查看trace发现binderservice耗时只占init耗时的一小部分&#xff0c;但是init逻辑并没有其他代码。 这里servicebind返回快的另一原因是se…