C++11:智能指针

文章目录

    • 前言
    • 正文
      • (1) 三种智能指针
      • (2) 智能指针的设计思路
      • (3) unique_ptr
        • unique_ptr的几种初始化方法
        • 获取unique_ptr的地址
        • unique_ptr的使用
      • (4) shared_ptr

前言

一般来说,我们想要在上开辟内存空间需要使用关键字new,但由于使用new之后我们还是需要进行手动释放,我们却常常忘记delete,或者在不该delete的地方delete了,这就会导致程序出错。
因此C++11推出了智能指针,让程序管理内存,让程序对new出来的内存进行自动释放(在析构智能指针的时候同时delete对象),这样就能避免很多因为粗心产生的bug。

正文

(1) 三种智能指针

这三种智能指针都在头文件memory中:

  1. unsigned_ptr:可以有多个指针指向同一个对象,其中包含了一个计数器
  2. unique_ptr:独占指向的对象
  3. weak_ptr:指向shared_ptr所管理的对象

(2) 智能指针的设计思路

  • 智能指针是类模板,在栈上创建智能指针对象
  • 将普通指针交给智能指针对象
  • 智能指针对象过期时,调用析构函数释放普通指针的内存

(其实还有个auto_ptr,但是在C++17中移除了,这里也就不说了)

(3) unique_ptr

它的意思是独享,也就是一个unique_ptr对象只对一个资源负责,当它析构的时候,指向的对象所分配的内存也随之释放。
因此,它在定义的时候禁用了拷贝构造函数复制构造函数

unique_ptr& operator=( const unique_ptr& ) = delete;
constexpr unique_ptr( std::nullptr_t ) noexcept = delete;

但是我们需要注意:指针和智能指针是两种类型!!!因此,我们不能直接使用智能指针接收new出来的对象:

#include<iostream>
#include<memory>int main(){// 正确std::unique_ptr<int> ptr(new int(5));// 正确,C++14的写法std::unique_ptr<int> ptr2 = std::make_ptr<int>(int(5));// 错误,不能直接使用智能指针接收直接new出来的对象std::unique_ptr<int> ptr3 = new int(10);// 错误,拷贝构造函数是已被删除的函数// 在VS中会显示该函数已被删除std::unique_ptr<int> temp_ptr(ptr);
}

它的底层原理其实也很简单:他的内部只有一个指针,这个指针指向的是它初始化时所指定的对象
这里写一段示例代码:

#include <iostream>
#include <ostream>
#include <memory>class AA{
public:// 利用友元进行运算符重载friend std::ostream& operator<<(std::ostream& os, const AA& temp);AA(){this->val = 0;std::cout << "调用了AA的构造函数" << std::endl;}AA(const int&& value){std::cout << "调用了AA的初始化构造函数" << std::endl;this->val = std::move(value); }~AA(){std::cout << "调用了AA的析构函数" << std::endl;}int showVal() const{return this->val;}private:int val;
};// 需要注意ostream是命名空间std的成员
// 并且我上面没有手动使用std::
// 需要注意的点:被const声明的变量只能使用const声明的成员函数
std::ostream& operator<<(std::ostream& os, const AA& temp){os << temp.showVal();return os;}int main(){AA* temp = new AA(100);std::cout << *temp << std::endl;std::unique_ptr<AA> auto_ptr(temp);std::cout << "main函数运行中" << std::endl;
}

通过输出语句的顺序,我们就能够理解它的原理:

调用了AA的初始化构造函数
100
main函数运行中
调用了AA的析构函数

它其实就是在智能指针进行析构的时候,其析构函数中再调用了其所指向对象的析构函,以此来释放所分配的内存
智能指针重载了输入输出运算符,因此我们可以像使用普通指针一样去使用智能指针
这里需要注意一个问题:不要用同一个指针(也叫裸指针或者原始指针)去初始化不同的unique_ptr对象。这个原因也很简单,就是重复释放内存的,跟深浅拷贝一样。

unique_ptr的几种初始化方法

这里我就直接给出代码吧:

#include <memory>
using namespace std;
int main(){unique_ptr<int> ptr1(new int(100)); // 分配内存并初始化unique_ptr<int> ptr2 = make_unique<int>(100); // C++14标准int temp = 100;unique_ptr<int> ptr3(temp); // 用已存在的地址初始化
}
获取unique_ptr的地址

在C++20之前,unique_ptr没有重载输出运算符,所以我们不能像使用普通指针那样输出智能指针所指向的地址:

#include <memory>
#include <iostream>
int main(){std::unique_ptr<int> ptr(new int(5));// 错误:unique_ptr不支持输出运算符std::cout << ptr;
}

想要获取其指向的地址,需要使用其成员函数get()

#include <memory>
#include <iostream>
int main(){std::unique_ptr<int> ptr(new int(5));// 错误:unique_ptr不支持输出运算符std::cout << ptr.get() << std::endl;std::cout << *ptr << std::endl;
}

由于智能指针为了模拟原始指针,而重载了"*“运算符和”->"运算符,因此我们能够像原始指针一样去使用它

unique_ptr的使用

这里需要重点强调的是unique_ptr作为参数进行函数传递的时候,我们都知道函数传参有两种方式:值传递引用传递
前面我们说到:由于unique_ptr是独享的,因此它禁用了拷贝构造函数复制构造函数,正是因此,unique_ptr在作为参数传递的时候只能使用引用传递:

#include <memory>
#include <iostream>void func(std::unique_ptr<int> ptr){std::cout << ptr.get() << "中,存储的数据是:" << *ptr << std::endl;
}int main(){std::unique_ptr<int> ptr = std::make_unique<int>(100);func(ptr);
}

上面这段函数会报错,正式因为使用的是值传递,只需要将函数传参变为传引用即可:

void func(std::unique_ptr<int>& ptr){}

(4) shared_ptr

std::shared_ptr底层和std::unique_ptr差不多,但它底层增加了一个计数器,用于计算共有多少个shared_ptr共享同一个对象

#include <memory>
#include <iostream>
int main(){std::shared_ptr<int> ptr = std::make_shared<int>(5);std::cout << "ptr = " << ptr.get() << std::endl;std::cout << "共有" << ptr.use_count() << "个shared_ptr指向" << ptr << std::endl;std::shared_ptr<int> ptr2 = ptr1;std::cout << "共有" << ptr.use_count() << "个shared_ptr指向" << ptr << std::endl;
}

知道了unique_ptr,shared_ptr就很好理解了。
现在我们来看看shared_ptr的常用方法:

  • get():和unique_ptr中的一样,获取指向底部管理着的对象的地址
  • use_count():获取当前shared_ptr中的计数器

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

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

相关文章

【数据结构】动态规划(Dynamic Programming)

一.动态规划&#xff08;DP&#xff09;的定义&#xff1a; 求解决策过程&#xff08;decision process&#xff09;最优化的数学方法。 将多阶段决策过程转化为一系列单阶段问题&#xff0c;利用各阶段之间的关系&#xff0c;逐个求解。 二.动态规划的基本思想&#xff1a; …

cpper绝不当炮灰选手-剑指大厂-c++后端面试大成攻略副本

针对于c后端&#xff0c;本文会直接从面试角度出发&#xff0c;盘点整理在c后端面试中出现的面试题型与经典题目。 包含&#xff1a; c/c&#xff08;36道&#xff09; 设计模式&#xff08;14道&#xff09; 数据结构与算法&#xff08;35道&#xff09; 操作系统&#xf…

【vue】点击导航菜单切换局部页面,打开展示默认栏目,页面刷新等问题

非专业前端&#xff0c;局限性较高&#xff0c;有些问题看起来很小&#xff0c;但是初次接触很棘手&#xff0c;需要查找很多博客&#xff0c;内容也很杂。以下只是过程中总结下来的&#xff0c;要解决的就是标题中的三个问题。 这是我需要达成的效果。 1.第一个是进入导航菜单…

浅谈web性能测试

什么是性能测试&#xff1f; web性能应该注意些什么&#xff1f; 性能测试&#xff0c;简而言之就是模仿用户对一个系统进行大批量的操作&#xff0c;得出系统各项性能指标和性能瓶颈&#xff0c;并从中发现存在的问题&#xff0c;通过多方协助调优的过程。而web端的性能测试…

Clion运行QT,模拟VS弹出CMD框打印

参考&#xff1a;https://stackoverflow.com/questions/35385772/running-clion-on-the-system-console-like-visual-studio 在运行配置的地方进行编辑&#xff1a; 可执行文件设置&#xff1a;C:\Windows\System32\cmd.exe程序实参&#xff1a;/c “start cmd.exe cmd /c “…

【开发板测评】一起玩转ACM32G103开发板,释放MCU无限潜能!

为帮助小伙伴们更好的快速熟悉了解ACM32G103系列的特性&#xff0c;航芯特别发起了该系列开发板评测试用&#xff0c;以帮助大家更好地运用MCU进行项目设计。 ACM32G103开发板介绍 ACM32G103系列是航芯推出的一款有着丰富模拟外设及安全存储扩展能力的高性价比通用MCU。 高性…

汉威科技全系列VOC气体检测产品,护航绿色低碳安全发展

可能很多人都不知道&#xff0c;危化品爆炸、城市光化学烟雾污染&#xff08;如英国伦敦烟雾事件&#xff09;、城市灰霾、温室效应、臭氧层空洞等问题背后的元凶都是VOC。VOC(Volatile Organic Compounds)即挥发性有机物&#xff0c;这类物质易挥发&#xff0c;且普遍具有毒性…

使用sqoop操作HDFS与MySQL之间的数据互传

一&#xff0c;数据从HDFS中导出至MySQL中 1&#xff09;开启Hadoop、mysql进程 start-all.sh/etc/init.d/mysqld start/etc/init.d/mysqld status 2&#xff09;将学生数据stu_data.csv传到HDFS的/local_student目录下 在hdfs中创建目录 hdfs dfs -mkdir /local_student 上…

系统调用过程

应用程序通过系统调用请求操作系统的服务。而系统中的各种共享资源都由操作系统内核统一掌管&#xff0c;因此凡是与共享资源有关的操作&#xff08;如存储分配、/O操作、文件管理等&#xff09;&#xff0c;都必须通过系统调用的方式向操作系统内核提出服务请求&#xff0c;由…

[实践总结] Java中读取properties配置文件

读取此key.properties文件 代码实现 import java.io.IOException; import java.io.InputStream; import java.util.Properties;public class PropertyUtils {private static final Properties properties new Properties();static {try (InputStream resourceAsStream Prope…

显示出所有留言信息,后期为了方便删记录用。

显示出所有留言信息&#xff0c;后期为了方便删记录用。 <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%> <% page import"java.util.Date" %> <% page import"java.text…

决策树 基尼系数算法

CART算法 CART Classification and Regression Tree(CART) 是决策树的一种用基尼指数来选择属性 (分类) &#xff0c;或用均方差来选择属性 (回归)顾名思义&#xff0c;CART算法既可以用于创建分类树&#xff0c;也可以用于创建回归树&#xff0c;两者在构建的过程中稍有差异。…

云上守沪 | 云轴科技ZStack成功实践精选(上海)

为打造国际数字之都&#xff0c;上海发布数字经济发展“十四五”规划&#xff0c;围绕数字新产业、数据新要素、数字新基建、智能新终端等重点领域&#xff0c;加强数据、技术、企业、空间载体等关键要素协同联动&#xff0c;加快进行数字经济发展布局&#xff1b;加快基础软件…

JavaWeb(五)

一、JDBC概述 JDBC 就是使用Java语言操作关系型数据库的一套API 全称是Java DataBase Connectivity 表示Java数据库连接。 JDBC的本质是官方&#xff08;sun公司&#xff09;定义的一套操作所有关系型数据库的规则&#xff0c;即接口,各个数据库厂商去实现这套接口&#xff0…

【zotero】搭配onedrive同步设置

1 登陆自己账户 登陆后记得取消文件同步的两个勾勾&#xff0c;不然后期会很占用储存空间的。 设置同步文件夹 一定要精准定位到绿色框框里头的文件夹。

存在重复元素算法(leetcode第217题)

题目描述&#xff1a; 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 &#xff0c;返回 true &#xff1b;如果数组中每个元素互不相同&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,1] 输出&#xff1a;true 示例 2&#xff1a;…

【动手学深度学习】(十一)卷积层

文章目录 一、从全连接到卷积 一、从全连接到卷积 分类猫和狗的图片 使用一个相机采集图片&#xff08;12M像素&#xff09;RGB图片有36M元素使用100大小的单隐层MLP&#xff0c;模型有3.6B元素 远多于世界上所有猫和狗总数&#xff08;900M狗&#xff0c;600M猫&#xff09;…

Python字符串格式化练习

示例一&#xff1a; 题目&#xff1a; 利用输出语句输出下列语句内容&#xff1a;公司&#xff1a; ikun股票,股票代码&#xff1a;001314 ,当前股价&#xff1a;2.5 &#xff0c;每日增长系数:1.2350 &#xff0c;经过增长7天后&#xff0c;股价达到了&#xff1a;10.95490…

关于ThreadLocal的理解

为什么threadLoacl要设计改为thread类里面有threadLocalMap&#xff1f; 为什么threadLoacl要设计改为thread类里面有threadLocalMap&#xff0c;然后map里面key为threadLoacl&#xff0c;value为value&#xff0c;而不是一个threadLoaclMap里面根据thread当key呢。 其实在早…

3.3 SaltStack 的部署和自动化配置

SaltStack 的部署和自动化配置 SaltStack 简介 介绍 SaltStack&#xff0c;一款功能强大的自动化配置管理工具&#xff0c;它采用分布式的代理-主控架构。强调 SaltStack 的目标&#xff1a;简化配置管理、加速任务执行、提高基础设施的可扩展性。 部署 SaltStack 环境 解释如…