c++ 智能指针实战分析

一.智能指针的设计思路

  1. 智能指针是类模板,再栈上创建智能指针对象。
  2. 把普通指针交给智能指针对象。
  3. 智能指针对象过期时,调用析构函数释放普通指针的内存。
  • 智能指针的类型
  1. auto_ptr是C++98的标准,c++17已经弃用。
  2. unique_ptr、shared_ptr和weak_ptr是C++11标准的。

二.智能指针 unique_ptr

C++智能指针unique_ptr是C++11标准库中提供的一种智能指针类型,用于管理动态分配的资源,主要用来避免内存泄漏和简化资源管理。unique_ptr是独占所有权的智能指针,即同一时间只能有一个unique_ptr指向特定的资源,当unique_ptr被销毁时,它所管理的资源会被自动释放。

1.基本用法

  1. 方法1:
unique_ptr<AA>p0(new AA("小明"));//分配内存并初始化
  1. 方法2:
unique_ptr<AA>p0=make_unique<AA>("小明");//c++14标准
  1. 方法3:
AA*p=new AA("小明");
unique_ptr<AA>p0(p);//用已存在的地址初始化
  1. 方法4:get()方法可以返回裸指针

2. 基本定义

  • 包含头文件
#include <memory>

以下是unique_ptr的简化版本的类模板定义(省略了部分实现细节):

template <typename T>
class unique_ptr {
private:
    T* ptr;

public:
    // 构造函数
    unique_ptr(T* p) : ptr(p) {}

    // 移动构造函数
    unique_ptr(unique_ptr&& other) noexcept : ptr(other.ptr) {
        other.ptr = nullptr;
    }

    // 析构函数
    ~unique_ptr() {
        delete ptr;
    }

    // 禁止拷贝和赋值
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;

    // 移动赋值运算符
    unique_ptr& operator=(unique_ptr&& other) noexcept {
        if (this != &other) {
            delete ptr;
            ptr = other.ptr;
            other.ptr = nullptr;
        }
        return *this;
    }

    // 获取裸指针
    T* get() const {
        return ptr;
    }

    // 重载运算符->
    T* operator->() const {
        return ptr;
    }

    // 重载解引用运算符*
    T& operator*() const {
        return *ptr;
    }

    // 释放资源
    void reset() {
        delete ptr;
        ptr = nullptr;
    }
};

3.实战测试

#include<iostream>
using namespace std;

#include<vector>
#include<memory>//智能指针头文件

class AA
{
public:
	string m_name;
	AA():m_name("未定义") { cout << m_name << "调用构造函数AA()" << endl; }

	AA(const string& name) :m_name(name) { cout << "调用构造函数(" << m_name << ")" << endl; }
	~AA() { cout << "调用了析构函数~AA(" << m_name << ")" << endl; }


};


int main()
{
	AA* p1 = new AA("小明");

	//使用智能指针来管理p

	//这里AA的意思是要管理的普通指针类型是AA
	//智能指针本来就是用来管理指针的所以不需要使用AA*
	unique_ptr<AA>ptr1(p1);

	//与正常指针用法一致
	cout << "m_name " << (*ptr1).m_name << endl;
	cout << "m_name " << ptr1->m_name << endl;
	cout << "m_name " << (*p1).m_name << endl;
	cout << "m_name " << p1->m_name << endl;

	//system("pause");不能使用这个,否则程序无法正常关闭,析构函数无法成功调用
	return 0;
}
  • 执行结果

在这里插入图片描述

三.智能指针 shared_ptr

在 C++ 中,智能指针是一种用于管理动态分配对象的指针类,可以帮助开发人员更好地管理内存资源,避免内存泄漏等问题。std::shared_ptr 是 C++11 标准库中提供的一种智能指针,用于共享所有权的指针管理。std::shared_ptr 可以自动追踪有多少个指针共享同一个对象,并在对象不再被引用时安全地释放内存。共享指针的引用计数会在创建、复制和销毁时进行递增和递减,当引用计数为 0 时,内存资源就会被释放。

使用方法:(和unique_ptr基本一样的)

#include <memory>
std::shared_ptr<int> sharedPtr = std::make_shared<int>(10);
智能指针的复制:
std::shared_ptr<int> sharedPtr2 = sharedPtr;
使用 std::make_shared 创建对象时,可以减少内存分配和额外的虚拟函数调用。

可以使用 .use_count() 方法获取当前共享指针的引用计数。

std::shared_ptr 还支持自定义的删除器,用于指定释放内存时的操作。

总的来说,std::shared_ptr 可以帮助避免潜在的内存泄漏和悬空指针问题,提高代码的健壮性和可维护性。

  • 实战测试
#include<iostream>
using namespace std;

#include<vector>
#include<memory>//智能指针头文件

class AA
{
public:
	string m_name;
	AA() :m_name("未定义") { cout << m_name << "调用构造函数AA()" << endl; }

	AA(const string& name) :m_name(name) { cout << "调用构造函数(" << m_name << ")" << endl; }
	~AA() { cout << "调用了析构函数~AA(" << m_name << ")" << endl; }


};


int main()
{
	AA* p1 = new AA("小明");

	//使用智能指针来管理p

	//这里AA的意思是要管理的普通指针类型是AA
	//智能指针本来就是用来管理指针的所以不需要使用AA*
	shared_ptr<AA>ptr1(p1);

	//与正常指针用法一致
	cout << "m_name " << (*ptr1).m_name << endl;
	cout << "m_name " << ptr1->m_name << endl;
	cout << "m_name " << (*p1).m_name << endl;
	cout << "m_name " << p1->m_name << endl;
	cout << "use_count=" << ptr1.use_count() << endl;

	//system("pause");不能使用这个,否则程序无法正常关闭,析构函数无法成功调用
	return 0;
}

  • 输出结果
    在这里插入图片描述

四.智能指针 weak_ptr

在 C++ 中,std::weak_ptr 是另一种智能指针,用于解决 std::shared_ptr 循环引用所带来的问题。std::weak_ptr 允许你观察指向的对象,但不拥有其所有权。通常与 std::shared_ptr 一起使用,可以避免循环引用导致的内存泄漏问题。

以下是一些关于 std::weak_ptr 的重要点和用法:

  1. 通过 std::weak_ptr 对象获std::shared_ptr 对象:
std::shared_ptr<int> sharedPtr = std::make_shared<int>(10);
std::weak_ptr<int> weakPtr = sharedPtr;
  1. 使用 std::lock_guard 和 std::shared_ptr::lock 方法来安全地获取 std::shared_ptr 对象,避免访问已经释放的对象:
if (std::shared_ptr<int> sharedPtr = weakPtr.lock()) {
    // 使用 sharedPtr 访问对象
} else {
    // 对象已被释放
}
  1. std::weak_ptr 不增加引用计数,只是提供了对对象的观察能力,也不会阻止对象的释放。

  2. 可以使用 std::weak_ptr 来打破循环引用,让对象能够被正确地释放。

std::weak_ptr 是一种很有用的工具,可以帮助解决在使用智能指针时可能会遇到的循环引用问题,确保内存资源能够正确地释放,提高程序的稳定性和可靠性。

  • 实战测试
#include<iostream>
using namespace std;

#include<vector>
#include<memory>//智能指针头文件

class BB;

class AA
{
public:
	string m_name;
	AA() :m_name("未定义") { cout << m_name << "调用构造函数AA()" << endl; }

	AA(const string& name) :m_name(name) { cout << "调用构造函数(" << m_name << ")" << endl; }
	~AA() { cout << "调用了析构函数~AA(" << m_name << ")" << endl; }

	shared_ptr<BB>p_B;

};

class BB
{
public:
	string m_name;
	BB() :m_name("未定义") { cout << m_name << "调用构造函数BB()" << endl; }

	BB(const string& name) :m_name(name) { cout << "调用构造函数(" << m_name << ")" << endl; }
	~BB() { cout << "调用了析构函数~BB(" << m_name << ")" << endl; }

	shared_ptr<AA>p_A;
};


int main()
{
	AA* p1 = new AA("小明");
	BB* p2 = new BB("李华");

	//使用智能指针来管理p

	//这里AA的意思是要管理的普通指针类型是AA
	//智能指针本来就是用来管理指针的所以不需要使用AA*
	shared_ptr<AA>ptr1(p1);
	shared_ptr<BB>ptr2 (p2);

	//与正常指针用法一致
	cout << "ptr1->m_name " << (*ptr1).m_name << endl;
	cout << "ptr1->m_name " << ptr1->m_name << endl;
	cout << "ptr1->m_name " << (*p1).m_name << endl;
	cout << "ptr1->m_name " << p1->m_name << endl;
	cout << "ptr1->use_count=" << ptr1.use_count() << endl;

	cout << "ptr2->m_name " << (*ptr2).m_name << endl;
	cout << "ptr2->m_name " << ptr2->m_name << endl;
	cout << "ptr2->m_name " << (*p2).m_name << endl;
	cout << "ptr2->m_name " << p2->m_name << endl;
	cout << "ptr2->use_count=" << ptr2.use_count() << endl;

	ptr1->p_B = ptr2;
	ptr2->p_A = ptr1;

	//system("pause");不能使用这个,否则程序无法正常关闭,析构函数无法成功调用
	return 0;
}
  • 由于相互执行都在等待对方释放资源,结果两方都无法释放资源
    在这里插入图片描述

  • 解决方法:使用weak_ptr

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从环型到树型:多种网络拓扑结构的优缺点及应用

网络拓扑作为网络设计的基础&#xff0c;对于网络的性能、可靠性和扩展性起着重要作用。作为网络通信工程师&#xff0c;我们不仅需要了解网络拓扑的基本概念&#xff0c;还需深入掌握其在实际网络设计中的应用。本文将详细介绍网络拓扑&#xff0c;包括物理拓扑、逻辑拓扑&…

怎么压缩pdf文件大小,如何压缩pdf文件大小

pdf文件怎么压缩&#xff1f;在当下这个信息爆炸的时代&#xff0c;无论是在工作场所还是校园中&#xff0c;我们经常会面临需要处理大文件的情况&#xff0c;而PDF格式作为一种保留文档结构和布局完整性的理想选择&#xff0c;有时候pdf文件太大&#xff0c;因此&#xff0c;对…

echarts 5.5.0版本下的层叠柱形图,每个值都从0开始,会有覆盖情况

需求&#xff1a; 1、每个公司&#xff0c;需要两个柱子去展示&#xff08;stack: 1是第一个柱子&#xff0c;stack:2,是第二个柱子&#xff09;&#xff1b; 2、必须每个数据都是从0开始&#xff0c;不在上一个值上累加&#xff1b; 3、鼠标滑上去的时候&#xff0c;最大值…

arco.design 利用 a-input-search 和 a-trigger 自己实现一个关键字查询select

先看效果 <div class"search-content" id"map-search-wrapper"><a-triggerpopup-visibleposition"bl"autoFitPopupWidth:popup-offset"4":unmount-on-close"true"trigger"click"popup-container"#m…

Netdiscover基本使用 - 发现局域网中存活主机

目录 0x00 介绍0x01 常用参数0x02 常用方式1. 主动模式2. 被动模式 0x00 介绍 原理&#xff1a;是一个二层&#xff08;数据链路层&#xff09;的ARP发现工具&#xff0c;执行命令的时候可以通过Wireshark查看是基于arp协议的。在不使用DHCP的无线网络上非常有用。 作用&#…

Go使用Gin框架开发的Web程序部署在Linux时,无法绑定监听Ipv4端口

最近有写一部分go语言开发的程序&#xff0c;在部署程序时发现&#xff0c;程序在启动后并没有绑定ipv4的端口&#xff0c;而是直接监听绑定ipv6的端口。 当我用netstat -antup | grep 3601查找我的gin服务启动的端口占用情况的时候发现&#xff0c;我的服务直接绑定了tcp6 &a…

Lua博客网站支持搜索、评论、登录注册

该简易博客示例用于学习网站的基础知识与MySQL数据库。 简述&#xff1a;开源Lua网站开发服务(FastWeb)支持&#xff1a;注册、登录、文章分页、评论分页、简易权限管理和搜索功能。发帖功能支持Markdown(支持记忆功能)图示&#xff1a;

GroundingDINO1.5突破开放式物体检测界限:介绍与应用

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

老板电器发布首个烹饪大模型“食神”,再次引领烹饪变革

爱因斯坦曾经说过&#xff1a;“我从不去想未来&#xff0c;因为它来得已经够快的了。”今天的人类社会&#xff0c;或许正处在一个连爱因斯坦都难以想象的巨变时代。一个没有任何高等数学或编程基础的普通人&#xff0c;只需一部手机或电脑&#xff0c;就可以享受苏格拉底的在…

【学习】开发板接口

工作用到机器的开发板 有如上三个接口 。最右是仿真器&#xff0c;中间是RS232串口&#xff0c;最左是电源线 仿真器 这个是仿真器 接入机器那端用的是SWD模式&#xff0c;另一端通过USB接电脑&#xff08;这小肥手拍的怪好看&#xff09;仿真口连接了四条线分别是 VCC&#…

基于 Spring Boot 的健康咨询系统

1 项目介绍 1.1 摘要 本项目旨在通过构建一个对用户更加友好的健康咨询平台&#xff0c;帮助用户方便、快捷地获取专业并且准确的健康咨询服务&#xff0c;同时为医疗机构提供一个高效易用的可以提供信息管理的服务平台。 项目采用了Spring Boot框架作为主要的开发平台。本系…

脚手架构建VUE项目

1.环境 安装node.js至少16以上&#xff0c;node中自动npm包管理工具 2.工具安装脚手架 在需要构建项目的目录下启动cmd,输入npm init vuelatest安装默认最新版本vue,根据提示完成安装。 3.安装依赖 安装提示安装依赖 4.项目构建成功&#xff0c;使用VScode工具打开

黑马程序员——Spring框架——day09——linux初级

目录&#xff1a; 前言 什么是Linux&#xff1f;为什么要学Linux 企业用人要求个人发展需要学完Linux能干什么 1).环境搭建2).常用命令3).安装软件4).项目部署小结2.Linux简介 主流操作系统Linux发展历史Linux系统版本Linux安装 安装方式介绍安装VMware安装Linux网卡设置安装S…

共创未来:订单共享模式驱动新零售增长新引擎

在当今快速变化的商业环境中&#xff0c;创新和效率成为了企业不可或缺的发展动力。为此&#xff0c;我们推出了一种颠覆性的商业模式——联合订单共享商业模式&#xff0c;它正在引领新零售行业的变革&#xff0c;并为企业家们提供了全新的发展机遇。 联合订单共享商业模式&am…

公司软件产品-资源详情列表中无法删除表(缺少删除按钮)

处理方式: 需要更新支撑后台库common_object表中STATE_FLAG 的字段状态 update common_object set STATE_FLAG 000000 where BASE_DIRECTORY 1460067;说明: 1460067 为目录ID 需要先将要删除的表结构迁移到一个新的目录中&#xff0c;迁移成功之后通过开发者工具f12查看dirI…

Spring底层原理之proxyBeanMenthod实例 动态代理 反射 Bean的拦截

proxyBeanMenthod 假设我们要进行一个系统的二次开发 然后第一次开发我们实用的是XML声明bean 二次开发的时候要用注解 我们如何把bean都加载上来呢 我们首先创建一个全新的配置类 package com.bigdata1421.config;public class SpringConfig32 { } 我们创建一个APP 加载…

Intentional设计分析,一款个人提效的AI产品

Intentional 是一款专注于提高工作效率的应用程序。 它允许用户设定上网目标&#xff0c;并会自动屏蔽与目标无关的网站&#xff0c;帮助用户保持专注&#xff0c;避免在网上无谓地浏览和分心。 这款应用程序是由开发者 Samy RAHIM 创造的&#xff0c;为了解决当今上网时容易…

AIPainter:创意绘画的智能助手

AIPainter 介绍 AIPainter是一款简单易用的AI画图工具&#xff0c;支持文生图、图生图&#xff08;提示词改图、图片变体、分辨率增强等&#xff09;&#xff0c;底层大模型基于开源的腾讯混元文生图、SDXL等。 功能特点 提示词库 AIPainter默认提供了一些常用场景的提示词供…

PLC梯形图(置位与复位)的使用方法

置位指令相当于我们把照明灯的开关按到开的状态,即便我们把手离开,开关也是通的,灯也是亮的。 想要关闭必须要把它按到关的状态,即使用复位指令。 复位指令相当于我们把照明灯的开关按到关的状态,把手离开,开关也是断的,灯也是不亮的。 想要打开必须要把它按到开的状…

【JPCS独立出版】2024计算建模与应用数学国际学术会议暨中俄微分方程及其应用学术会议(CMAM 2024 DEA,8月2-4)

2024计算建模与应用数学国际学术会议暨中俄微分方程及其应用学术会议&#xff08;CMAM 2024 & DEA&#xff09;由大连海事大学理学院主办&#xff0c;上海海关学院、俄罗斯科学院科学城数学中心、辽宁省数学学会、大连市数学学会协办&#xff0c;AEIC学术交流中心承办。会议…