独占指针:unique_ptr 与 函数调用 笔记

推荐B站视频:

2.unique_ptr_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV18B4y187uL?p=2&vd_source=a934d7fc6f47698a29dac90a922ba5a3

3.unique_ptr与函数调用_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV18B4y187uL?p=3&vd_source=a934d7fc6f47698a29dac90a922ba5a3一、独占指针:unique_ptr 

  • 任何给定的时刻,只能有一个指针管理内存
  • 当指针超出作用域时,内存将自动释放
  • 该类型指针不可Copy,只可以Move

二、三种创建方式

  • 通过已有裸指针创建
  • 通过new来创建
  • 通过std::make_unique创建(推荐)

准备好头文件和源文件:

  • cat.h
#ifndef CAT_H
#define CAT_H
#include <string>
#include <iostream>
class Cat{
public:Cat(std::string name);Cat() = default;~Cat();void catInfo() const {std::cout<<"cat info name : "<<m_name<<std::endl;}std::string get_name() const{return m_name;}void set_cat_name(const std::string &name) {this->m_name = name;}
private:std::string m_name{"Mimi"};
};
#endif
  • cat.cpp
#include "cat.h"
Cat::Cat(std::string name) :m_name(name) {std::cout<<"Constructor of Cat : "<<m_name<<std::endl;
}Cat::~Cat() {std::cout<<"Destructor of Cat"<<std::endl;
}

1.通过已有裸指针创建 

(1)在栈上

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// stack 在main函数下,会在最后一条语句之后再执行析构函数Cat c1("maomao");c1.catInfo();{ // 在这个作用域下,结束后会自动销毁Cat c1("Tom");c1.catInfo();}cout<<"over~"<<endl; return 0;
}

执行结果:

PS D:\Work\C++UserLesson\cppenv\build> ."D:/Work/C++UserLesson/cppenv/build/app.exe"
Constructor of Cat : maomao
cat info name : maomao
Constructor of Cat : Tom
cat info name : Tom
Destructor of Cat
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\build>

 (2)在堆上

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全Cat* c_p1 = new Cat("Jerry");c_p1->catInfo();{   Cat* c_p1 = new Cat("JiaFeiMao");c_p1->catInfo();}c_p1->catInfo();cout<<"over~"<<endl; return 0;
}

执行结果:

PS D:\Work\C++UserLesson\cppenv\build> ."D:/Work/C++UserLesson/cppenv/build/app.exe"
Constructor of Cat : Jerry
cat info name : Jerry
Constructor of Cat : JiaFeiMao
cat info name : JiaFeiMao
cat info name : Jerry
over~
PS D:\Work\C++UserLesson\cppenv\build>
  • 没有执行析构函数,怎么办呢?只能手动释放
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全Cat* c_p1 = new Cat("Jerry");c_p1->catInfo();{   Cat* c_p1 = new Cat("JiaFeiMao");c_p1->catInfo();// 需要手动释放delete c_p1;c_p1 = nullptr; // 防止野指针}c_p1->catInfo();// 需要手动释放delete c_p1;c_p1 = nullptr; // 防止野指针cout<<"over~"<<endl; return 0;
}

执行结果: 

PS D:\Work\C++UserLesson\cppenv\build> ."D:/Work/C++UserLesson/cppenv/build/app.exe"
Constructor of Cat : Jerry
cat info name : Jerry
Constructor of Cat : JiaFeiMao
cat info name : JiaFeiMao
Destructor of Cat
cat info name : Jerry
Destructor of Cat
over~
PS D:\Work\C++UserLesson\cppenv\build>

接着再来一个例子:

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全int* i_p1 = new int(100);{   i_p1 = new int(200);}cout<<*i_p1<<endl;// 很糟糕,说明没有自动回收该资源cout<<"over~"<<endl; return 0;
}

执行结果:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
200
over~
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

没有执行析构函数,怎么办呢?只能手动释放

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全int* i_p1 = new int(100);{   i_p1 = new int(200);// 需要手动释放delete i_p1;    i_p1 = nullptr;}cout<<"over~"<<endl; return 0;
}

但手动释放的时候,还再多释放一次或者再访问会崩溃

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全int* i_p1 = new int(100);{   i_p1 = new int(200);// 需要手动释放delete i_p1;    i_p1 = nullptr;}cout<<*i_p1<<endl;// 需要手动释放delete i_p1;    i_p1 = nullptr;cout<<"over~"<<endl; return 0;
}

总结:roll pointer 原始指针 非常不安全

2.通过new来创建

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;int main(int argc,char* argv[]) {// unique_point  Cat* c_p2 = new Cat("ruite");std::unique_ptr<Cat> u_c_p2(c_p2);// c_p2还能用吗?// 否则如下:无法独占c_p2->catInfo();                 //ruiteu_c_p2->catInfo();               //ruitec_p2->set_cat_name("lanmao");u_c_p2->catInfo();               //lanmaocout<<"over~"<<endl; return 0;
}

执行结果:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : ruite
cat info name : ruite
cat info name : ruite
cat info name : lanmao
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

解决无法独占的问题:

  • 方法一:
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// unique_point  Cat* c_p2 = new Cat("ruite");std::unique_ptr<Cat> u_c_p2(c_p2);c_p2 = nullptr;u_c_p2->catInfo();cout<<"over~"<<endl; return 0;
}

执行结果: 

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : ruite
cat info name : ruite
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
  • 方法二:
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// 第二种 newstd::unique_ptr<Cat> u_c_p3{new Cat("hongmao")};u_c_p3->catInfo();u_c_p3->set_cat_name("heimao");u_c_p3->catInfo();cout<<"over~"<<endl; return 0;
}

执行结果:  

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : hongmao
cat info name : hongmao
cat info name : heimao
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

 3.通过std::make_unique创建(推荐)

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// 推荐创建方式 第三种 std::move_uniquestd::unique_ptr<Cat> u_c_p4 = make_unique<Cat>();u_c_p4->catInfo();u_c_p4->set_cat_name("huangmao");u_c_p4->catInfo();cout<<"over~"<<endl; return 0;
}

执行结果:  

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
cat info name : Mimi
cat info name : huangmao
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

三、get()与->和解引用

- unique_ptr可以通过get()获取地址
- unique_ptr实现了->与*- 可以调用->调用成员函数- 可以调用*调用 deferencing
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// 推荐创建方式 第三种 std::move_uniquestd::unique_ptr<Cat> u_c_p4 = make_unique<Cat>();std::unique_ptr<int> u_i_p4 = make_unique<int>(200);u_c_p4->catInfo();u_c_p4->set_cat_name("huangmao");u_c_p4->catInfo();cout<<"============================"<<endl;cout<<*u_i_p4<<endl;cout<<"int address: "<<u_i_p4.get()<<endl;cout<<"Cat address: "<<u_c_p4.get()<<endl;cout<<"over~"<<endl; return 0;
}

执行结果:   

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
cat info name : Mimi
cat info name : huangmao
============================
200
int address: 00000257966B71C0
Cat address: 00000257966C3450
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

四、unique_ptr 与函数调用

第三节:unique_ptr与函数调用- unique_ptr是不可Copy,只可以Move- 在做函数参数或返回值中一定要注意所有权函数调用与unique_ptr注意事项- Passing by value- 需要用std::move来转移内存拥有权- 如果参数直接传入std::make_unique语句,自动转换为move- Passing by reference- 如果设置参数为const则不能改变指向- 比如说reset()- reset()方法为智能指针清空方法- Return by value- 指向一个local object- 可以用作链式函数

1.pass by value

(1)需要用std::move来转移内存拥有权

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_value(std::unique_ptr<Cat> c) {c->catInfo();
} int main(int argc,char* argv[]) {// 1.pass by valuestd::unique_ptr<Cat> c1 = make_unique<Cat>("Tom");do_with_cat_pass_value(std::move(c1));return 0;
}

运行结果:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : Tom
cat info name : Tom
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

如果在do_with_cat_pass_value(std::move(c1));多加一句:c1->catInfo(); // 此时c1已经无效

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_value(std::unique_ptr<Cat> c) {c->catInfo();
} int main(int argc,char* argv[]) {// 1.pass by valuestd::unique_ptr<Cat> c1 = make_unique<Cat>("Tom");do_with_cat_pass_value(std::move(c1));c1->catInfo(); // 此时c1已经无效return 0;
}

运行结果:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : Tom
cat info name : Tom
Destructor of Cat
cat info name :
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

(2)如果参数直接传入std::make_unique语句,自动转换为move

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_value(std::unique_ptr<Cat> c) {c->catInfo();
} int main(int argc,char* argv[]) {do_with_cat_pass_value(make_unique<Cat>("JiaFeiMao")); //movereturn 0;
}

运行结果:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : JiaFeiMao
cat info name : JiaFeiMao
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
  • 总结:如果是pass by value ,传进去以后,这个值就不存在了。它的所有权就转移了,转移到函数里边,函数结束之后就直接消失了 

2.pass by reference

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_ref(std::unique_ptr<Cat>& c) {c->set_cat_name("TomCat");c->catInfo();c.reset();
}int main(int argc,char* argv[]) {// 2. pass by ref// (不加const)std::unique_ptr<Cat> c2 = make_unique<Cat>("heimao");do_with_cat_pass_ref(c2); // c2->catInfo(); // 此时c2已经无效cout<<"address: "<<c2.get()<<endl;return 0;
}

 运行过程:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : heimao
cat info name : TomCat
Destructor of Cat
address: 0000000000000000
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

 注意:如果do_with_cat_pass_ref函数里不添加c.reset();那么c2->catInfo();还是可以的

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_ref(std::unique_ptr<Cat>& c) {c->set_cat_name("TomCat");c->catInfo();// c.reset();
}int main(int argc,char* argv[]) {// 2. pass by ref// (不加const)std::unique_ptr<Cat> c2 = make_unique<Cat>("heimao");do_with_cat_pass_ref(c2); c2->catInfo(); cout<<"address: "<<c2.get()<<endl;return 0;
}

运行结果:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : heimao
cat info name : TomCat
cat info name : TomCat
address: 0000018F1CF14640
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

也可以加上const,使得do_with_cat_pass_ref函数内不能使用reset

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_ref(const std::unique_ptr<Cat>& c) {c->set_cat_name("TomCat");c->catInfo();// c.reset();// 如果加上了const,无法使用reset()
}int main(int argc,char* argv[]) {// 2. pass by ref// (不加const)std::unique_ptr<Cat> c2 = make_unique<Cat>("heimao");do_with_cat_pass_ref(c2); c2->catInfo(); cout<<"address: "<<c2.get()<<endl;return 0;
}

运行结果:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : heimao
cat info name : TomCat
cat info name : TomCat
address: 000001D2F5343170
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

 链式调用

#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;std::unique_ptr<Cat> get_unique_ptr() {std::unique_ptr<Cat> p_cat = std::make_unique<Cat>("local cat");p_cat->catInfo(); // 输出: local catcout<<p_cat.get()<<endl;cout<<&p_cat<<endl;p_cat->set_cat_name("TomCat");p_cat->catInfo(); // 输出: TomCatreturn p_cat;
}int main(int argc,char* argv[]) {get_unique_ptr()->catInfo();cout<<"over~"<<endl;return 0;
}

运行结果:

PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : local cat
cat info name : local cat
0000023AC98439B0
00000077859AF7C8
cat info name : TomCat
cat info name : TomCat
Destructor of Cat
over~
PS D:\Work\C++UserLesson\cppenv\bin\Debug>

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

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

相关文章

【MyBatis】#{} 和 ${}

目录 1. #{} 使用示例&#xff1a; 2. ${} 使用示例&#xff1a; SQL注入 使用#{}的情况&#xff1a; 使用${}的情况&#xff1a; MyBatis是一种用于Java语言的持久层框架&#xff0c;它简化了数据库操作的过程。在MyBatis中&#xff0c;我们经常会看到两种不同的参数占…

UDF学习(三)数据访问宏

数据访问宏一 网格节点相关宏** NODE_X (v) 节点v的x方向的坐标 &#xff08;Node *v&#xff09; NODE_Y (v) 节点v的y方向的坐标 &#xff08;Node *v&#xff09; NODE_Z (v) 节点v的z方向的坐标 &#xff08;Node *v&#xff09; F_NODE (f,t,n) 获取节点 (face_t f, Thre…

vivado 放置I/O端口

放置I/O端口 I/O规划视图布局提供了几种将I/O端口分配给封装引脚的方法。你可以在“I/O端口”窗口中选择单个I/O端口、I/O端口组或接口&#xff0c;然后分配将它们封装到封装窗口中的封装引脚或设备窗口中的I/O焊盘。在“程序包”窗口中&#xff0c;您可以&#xff1a; •将端…

基于springboot+vue的明星周边产品销售网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

算子:详细篇

目录 一、执行环境 1.1 创建执行环境 1.2 执行模式 二、源算子 2.1 从集合中读取数据 2.2 从文件读取数据 2.3 从socket读取数据 2.4 从kafka读取数据 三、转换算子 3.1 基本转换算子 &#xff08;1&#xff09;映射(map) &#xff08;2&#xff09;过滤(filter) &#xff08…

用友U8接口-系统管理(3)

教程目录 部署和简要说明(1) 获取token&数据字段(2) 概括 本文的操作需要正确部署U8HttpApi对本套接口系统管理目录说明 系统管理 获取token 参考获取token 根据sql进行查询 此POST方式接口运行调用者传入SQL语句&#xff0c;或者将SQL语句写到xml文件中&#xff0…

软件测试20个基础面试题及答案

什么是软件测试&#xff1f; 答案&#xff1a;软件测试是指在预定的环境中运行程序&#xff0c;为了发现软件存在的错误、缺陷以及其他不符合要求的行为的过程。 软件测试的目的是什么&#xff1f; 答案&#xff1a;软件测试的主要目的是保证软件的质量&#xff0c;并尽可能…

mysql INSERT数据覆盖现有元素(若存在)

INSERT...ON DUPLICATE KEY UPDATE的使用 如果指定了ON DUPLICATE KEY UPDATE&#xff0c;并且插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值&#xff0c;则会更新ON DUPLICATE KEY UPDATE关键字后面的字段值。 例如&#xff0c;如果列a被定义为UNIQUE&#xff0…

OSI七层模型 | TCP/IP模型 | 网络和操作系统的联系 | 网络通信的宏观流程

文章目录 1.OSI七层模型2.TCP/IP五层(或四层)模型3.网络通信的宏观流程3.1.同网段通信3.2.跨网段通信 1.OSI七层模型 在计算机通信诞生之初&#xff0c;不同的厂商都生产自己的设备&#xff0c;都有自己的网络通讯标准&#xff0c;导致了不同厂家之间各种协议不兼容&#xff0…

AI数字人-数字人视频创作数字人直播效果媲美真人

在科技的不断革新下&#xff0c;数字人技术正日益融入到人们的生活中。近年来&#xff0c;随着AI技术的进一步发展&#xff0c;数字人视频创作领域出现了一种新的创新方式——AI数字人。数字人视频通过AI算法生成虚拟主播&#xff0c;其外貌、动作、语音等方面可与真实人类媲美…

v43-47.problems

1.for循环 一般地&#xff0c;三步走&#xff1a; for&#xff08;初始化&#xff1b;表达式判断&#xff1b;递增/递减&#xff09; &#xff5b; ....... &#xff5d; 但是&#xff0c;如果说声明了全局变量&#xff0c;那么第一步初始化阶段可以省略但是要写分号‘ ; ’…

串口WiFi模块简介、工作原理、工作方式等8大知识点

WiFi模块又名串口WiFi模块&#xff0c;属于物联网传输层&#xff0c;功能是将串口或TTL电平转为符合WiFi无线网络通信标准的嵌入式模块&#xff0c;内置无线网络协议IEEE802.11b.g.n协议栈以及TCP/IP协议栈。串口WIFI模块&#xff0c;体积小&#xff0c;功耗低&#xff0c;采用…