了解c++11新特性-智能指针

c++智能指针概念

1 智能指针的思想--C++RAII机制

        RAII是Resource Acquisition Is Initialization(wiki上面翻译成 “资源获取就是初始化”)的简称,是C++语言的一种管理资源、避免泄漏的惯用法,利用的就是C++构造的对象最终会被析构销毁的原则。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,在对象生命期结束析构的时候,释放构造时获取的资源。

        我们知道,c++动态内存分配需要为所有的new 分配的内存手动delete掉对应普通指针指向的内存资源,否则可用内存可能会随着代码的调用越来越少,最终导致程序崩溃。

        我们也知道,当我们在一个函数内部使用局部变量,当退出了这个局部变量的作用域时,这个变量也就别销毁了;当这个变量是类对象时,这个时候,就会自动调用这个类的析构函数,而这一切都是自动发生的,不要程序员显式的去调用完成。

        那么这个也太好了,智能指针思想就是这样去完成的。智能指针就是利用RAII(Resource Acquisition Is Initialization)机制对普通指针进行的一层封装。这样使得智能指针的行为动作像一个指针,本质上却是一个对象,这样可以安全的管理一个对象的生命周期。

        智能指针 = 普通指针 + c++对象的构造/析构机制

2 智能指针种类

在c++中,智能指针一共定义了4种:
        unique_ptr、shared_ptr、weak_ptr 和 auto_ptr。其中,auto_ptr 在 C++11已被摒弃,在C++17中已经移除不可用,重点学习 前两种。这些类型都定义在头文件<memory>中。

std::weak_ptr: std::weak_ptr是一种弱引用智能指针,它用于解决std::shared_ptr可能导致的循环引用问题。std::weak_ptr可以观测到被std::shared_ptr管理的对象,但不拥有该对象,也不会增加引用计数。它可以通过std::shared_ptr进行构造或转换,用于临时访问被std::shared_ptr管理的对象,但不会延长对象的生命周期。

c++智能指针介绍

unique_ptr

        unique_ptr是一种独占智能指针,它提供了对动态分配的对象的独占所有权。它不允许多个unique_ptr 指向同一块内存,因此不能进行拷贝,只能进行移动(转移交出所有权给另一个智能指针)。当std::unique_ptr超出其作用域时,或者通过std::move将所有权转移给其他std::unique_ptr时,它指向的对象也会被自动摧毁,帮助程序员实现了自动释放的功能。

        unique_ptr 也可能还未指向对象,这时的状态被称为 empty。

        unique_ptr对象封装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。

创建unique_ptr:

可以先看一个简单的 unique_ptr 使用的例子:

动态创建一个一维数组 

unique_ptr<char> p(new char[10]);

        此时我们只要unique_ptr创建成功,unique_ptr对应的析构函数都能保证被调用,从而保证申请的动态资源能被释放掉。

对比-用普通指针用法:

char* p = new char[10];//开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址
delete[] p;

下面可以更详细的了解unique_ptr的构造和析构原型:

unique_ptr的构造方法:
// non-specialized 
template <class T, class D = default_delete<T>> class unique_ptr;
// array specialization   
template <class T, class D> class unique_ptr<T[],D>;

其中 T 指其管理的对象类型,D 指该对象销毁时所调用的释放方法,可以使用自定义的删除器,他也有一个默认的实现,即 detele 操作。

// unique_ptr constructor example
#include <iostream>
#include <memory>int main () {std::default_delete<int> d;std::unique_ptr<int> u1;std::unique_ptr<int> u2 (nullptr);std::unique_ptr<int> u3 (new int);std::unique_ptr<int> u4 (new int, d);std::unique_ptr<int> u5 (new int, std::default_delete<int>());std::unique_ptr<int> u6 (std::move(u5));std::unique_ptr<int> u7 (std::move(u6));std::unique_ptr<int> u8 (std::auto_ptr<int>(new int));std::cout << "u1: " << (u1?"not null":"null") << '\n';std::cout << "u2: " << (u2?"not null":"null") << '\n';std::cout << "u3: " << (u3?"not null":"null") << '\n';std::cout << "u4: " << (u4?"not null":"null") << '\n';std::cout << "u5: " << (u5?"not null":"null") << '\n';std::cout << "u6: " << (u6?"not null":"null") << '\n';std::cout << "u7: " << (u7?"not null":"null") << '\n';std::cout << "u8: " << (u8?"not null":"null") << '\n';return 0;
}//执行结果:
u1: null
u2: null
u3: not null
u4: not null
u5: null
u6: null
u7: not null
u8: not null

注意:不能像下面这样写。new得到的是一个普通指针,智能指针本质是一个对象,不能直接赋值,而是作为参数传递。

std::unique_ptr<int> p = new int(5)

但可以

shared_ptr<int> p = make_shared<int>(5);
自定义unique_ptr的析构方法 std::unique_ptr::~unique_ptr
// unique_ptr destructor example
#include <iostream>
#include <memory>int main () {// user-defined deleterauto deleter = [](int*p){delete p;std::cout << "[deleter called]\n";};std::unique_ptr<int,decltype(deleter)> foo (new int,deleter);std::cout << "foo " << (foo?"is not":"is") << " empty\n";return 0; // [deleter called]
}//输出
foo is not empty
[deleter called]
unique_ptr 不能被拷贝,但可以被移动-例子:

        因为不允许多个unique_ptr 指向同一块内存,因此不能进行拷贝,只能进行移动。所以unique_ptr也没有复制构造函数,不支持普通的拷贝和赋值操作。

std::unique_ptr<int>p1(new int(5));
std::unique_ptr<int>p2=p1;// 编译会出错
std::unique_ptr<int>p3=std::move(p1);// 转移所有权, 现在那块内存归p3所有, p1成为无效的针.
p3.reset();//释放内存.
p1.reset();//无效

shared_ptr

        我们提到的智能指针,很大程度上就是指的shared_ptr,shared_ptr也在实际应用中广泛使用,因为它的性质基本跟我们使用的普通指针一致。

        它是一种引用计数智能指针,它允许多个指针共享同一块内存。每当创建一个std::shared_ptr指向同一个对象时,引用计数会增加。当释放一个指向该对象的std::shared_ptr时,引用计数会减少 1 个,当引用计数为零时,指向的内存会被自动释放。

        显然,share_ptr可以进行赋值拷贝。
 

创建shared_ptr
shared_ptr<int> p = make_shared<int>(1);
shared_ptr<int> p2(p);
shared_ptr<int> p3 = p;
make_shared

        make_shared 是 c++11 加入标准库的,专门用于生成独占型指针 std::shared_ptr  的模板函数。使用示例:

int  main()
{// 1.shared_ptr<string> p1(new string("66888"));cout << *p1 << endl;// 2.shared_ptr<string> p2 = make_shared<string>("888888");cout << *p2 << endl;return 0;
}

        我们更推荐使用 make_shared 创建 shared_ptr,性能和安全性更高。unique_ptr也同理,建议使用make_unique来创建。详细原因可参考:

C++ 学习系列 -- 智能指针 make_shared 与 make_unique_make_unique和make_shared-CSDN博客

通过智能指针的 get() 成员函数获得 其封装的普通指针: 
void func() {shared_ptr<int> p = make_shared<int>(1);int *p2 = p.get();cout<<*p2<<endl;
}

博文参考来源:

C++RAII机制:C++RAII机制-CSDN博客

智能指针:

c++智能指针详解_bitcarmanlee的博客-CSDN博客

智能指针 unique_ptr 详解-CSDN博客

cpp中易混淆总结(面经总结)-CSDN博客

C++ 学习系列 -- 智能指针 make_shared 与 make_unique_make_unique和make_shared-CSDN博客

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

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

相关文章

如何在Go中编写包

包由位于同一目录中的Go文件组成,这些文件在开头具有相同的package语句。你可以从包中包含额外的功能,使程序更复杂。有些包可以通过Go标准库获得,因此与Go安装一起安装。其他可以使用Go的go get命令安装。您还可以通过使用必要的package语句在要共享代码的相同目录中创建Go…

【Vue第3章】使用Vue脚手架_Vue2_笔记

笔记 脚手架文件结构 ├── node_modules ├── public │ ├── favicon.ico: 页签图标 │ └── index.html: 主页面 ├── src │ ├── assets: 存放静态资源 │ │ └── logo.png │ │── component: 存放组件 │ │ └── HelloWorld.vue …

英语学习(衣服与服装篇)

一、购买服装 1.有关时尚的形容词 1&#xff09;有许多可用于形容 fashion 和 clothes 的形容词。 cool 酷的 stylish 时髦的 in style 时髦 fashionable 时髦的&#xff0c;流行的 2&#xff09;描述不喜欢的衣服 out of style 过时的 dre…

springboot和swagger版本不兼容问题解决

1.错误提示 org.springframework.context.ApplicationContextException: Failed to start bean documentationPluginsBootstrapper; nested exception is java.lang.NullPointerExceptionat org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLi…

Java程序员,你掌握了多线程吗?

文章目录 01 多线程对于Java的意义02 为什么Java工程师必须掌握多线程03 Java多线程使用方式04 如何学好Java多线程写作末尾 摘要&#xff1a;互联网的每一个角落&#xff0c;无论是大型电商平台的秒杀活动&#xff0c;社交平台的实时消息推送&#xff0c;还是在线视频平台的流…

ConcurrentHashMap实现线程安全原理

我们知道&#xff0c;在日常开发中使用的HashMap是线程不安全的&#xff0c;而线程安全类HashTable只是简单的在方法上加锁实现线程安全&#xff0c;效率低下&#xff0c;所以在线程安全的环境下我们通常会使用ConcurrentHashMap。 1. 初始化数据结构时的线程安全 HashMap的底…

【51单片机系列】矩阵按键扩展实验

本文对矩阵按键的一个扩展&#xff0c;利用矩阵按键和动态数码管设计一个简易计算器。代码参考&#xff1a;https://blog.csdn.net/weixin_47060099/article/details/106664393 实现功能&#xff1a;使用矩阵按键&#xff0c;实现一个简易计算器&#xff0c;将计算数据及计算结…

【Proteus仿真】【51单片机】简易计算器

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使动态数码管、矩阵按键、蜂鸣器等。 主要功能&#xff1a; 系统运行后&#xff0c;数码管默认显示0&#xff0c;输入对应的操作数进行四则运算&#x…

JavaScript <关于逆向RSA非对称加密算法的案例(附原代码)>--案例(五)

前言: 趁热打铁,标记一下RSA的算法逆向...第二篇会有详解(本篇重在过程) 正文: 废话不说,直接分析步骤图: 到了这里,可以看到在登录的时候,需要验证码(本篇不教反验证码) 下面是正题--->逆他的pwd(密码) 总结: 问题:怎么确定一个密文数据是基于什么算法做出来的呢? 答:…

当初你问我为什么选择计算机?回头现在来总结就是穷

还记得当初自己为什么选择计算机&#xff1f; 当初你问我为什么选择计算机&#xff0c;我笑着回答&#xff1a;“因为我梦想成为神奇的码农&#xff01;我想像编织魔法一样编写程序&#xff0c;创造出炫酷的虚拟世界&#xff01;”谁知道&#xff0c;我刚入门的那天&#xff0…

NAS外网访问方案

基础流程 路由器开启端口映射&#xff08;如果有猫则要配置猫为转发模式&#xff0c;由路由器直接拨号即可使用第三方程序让内网ip发布到公网上&#xff08;如果有云服务器&#xff09;需要开启防火墙端口 好用的第三方程序 FRP穿透 优点&#xff1a;开源免费&#xff0c;速…

期末速成数据库极简版【创建】(1)

目录 前言 【1】T-SQL语句创建数据库 【2】T-SQL语句删除数据库 【3】T-SQL语句创建表 完整性约束 数据类型 例子 【4】T-SQL语句修改表 【5】T-SQL语句删除表 关于数据库&#xff0c;在我们学习Linux网络编程后面会详细学习到&#xff0c;为了应付期末考试&#xff0…

[MySQL] MySQL中的内置函数

本篇文章主要是对MySQL中常见的内置函数进行了详细解释。例如有日期类函数、字符串类函数、数学类函数等等。希望本篇文章会对你有所帮助。 文章目录 一、日期类函数 1、1 使用详解 1、2 实例演示 二、字符串函数 2、1 使用详解 2、2 实例演示 三、数学函数 四、其他函数 &…

销售如何开发客户?

在销售过程中&#xff0c;开发客户是至关重要的一环。只有拥有足够的客户群体&#xff0c;才能为公司带来更多的业务机会和收入。 现如今&#xff0c;不管是哪一行竞争都十分激烈&#xff0c;特别是那些本身没有核心竞争力和核心技术的传统 to b企业&#xff0c;正处于十分尴尬…

Leo赠书活动-12期 【Java程序员,你掌握了多线程吗?】文末送书

Leo赠书活动-12期 【Java程序员&#xff0c;你掌握了多线程吗&#xff1f;】文末送书 ✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1…

socket 套接字

1、套接字介绍 socket起源于Unix&#xff0c;遵循“一切皆文件”出发点&#xff0c;都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。 在设计模式中&#xff0c;Socket把复杂的TCP/IP协议族隐藏在Socket接口后面&#xff0c;Socket去组织数据&#xf…

Pytorch线性回归教程

import torch import numpy as np import torch.nn as nn import matplotlib.pyplot as plt生成测试数据 # 长期趋势 def trend(time, slope0):return slope * time# 季节趋势 def seasonal_pattern(season_time):return np.where(season_time < 0.4,np.cos(season_time * …

vue3 + mark.js 实现文字标注功能

效果图 安装依赖 npm install mark.js --save-dev npm i nanoid代码块 <template><!-- 文档标注 --><header><el-buttontype"primary":disabled"selectedTextList.length 0 ? true : false"ghostclick"handleAllDelete"…

Linux学习笔记2

web服务器部署&#xff1a; 1.装包&#xff1a; [rootlocalhost ~]# yum -y install httpd 2.配置一个首页&#xff1a; [rootlocalhost ~]# echo i love yy > /var/www/html/index.html 启动服务&#xff1a;[rootlocalhost ~]# systemctl start httpd Ctrl W以空格为界…

String、StringBuffer、StringBuilder

String类 特点&#xff1a; 1.类由final关键字修饰&#xff0c;不可被继承&#xff1b; 2.value是一个由final修饰的字符数组&#xff0c;即字符串的长度不可修改&#xff1b; 3.实现了Comparable<T>接口&#xff0c;可进行比较&#xff1b; StringBuffer 特点&#x…