智能指针知识点归纳

3.3 智能指针
3.3.1 RAII 和 智能指针实现

智能指针使用RAII 技术将普通的指针封装为一个栈对象,当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。

智能指针的实现:

template <typename T>
class SharedPtr {
public:explicit SharedPtr(T* ptr = nullptr) : data(ptr), refCount(new int(1)) {}SharedPtr(const SharedPtr<T>& other) : data(other.data), refCount(other.refCount) {(*refCount)++;}~SharedPtr() {if (--(*refCount) == 0) {delete data;delete refCount;}}SharedPtr<T>& operator=(const SharedPtr<T>& other) {if (this != &other) {if (--(*refCount) == 0) {delete data;delete refCount;}data = other.data;refCount = other.refCount;(*refCount)++;}return *this;}T& operator*() const {return *data;}T* operator->() const {return data;}private:T* data;int* refCount;
};
3.3.2 shared_ptr

shared_ptr 除了内置的常见属性,常见的有两种用法:一种是使用std::shared_ptr<void>用于线程池、任务队列封装任务;另一种用于类成员智能指针;

(1)shared_ptr

​ shared_ptr 共享指针主要有个用法:reset,get, use_count;创建使用std::make_shared

std::vector<double> vec = {2, 3, 1, 4, 6, 8, 9, 7, 5, 0};
std::shared_ptr<std::vector<double>>sharePtr = std::make_shared<std::vector<double>>(vec);
std::cout << "use_count is: " << sharePtr.use_count() << std::endl;
std::vector<double> *ptr = sharePtr.get();
for(int i = 0; i<ptr->size(); i++){std::cout << (*ptr)[i] << ", ";
}
std::cout << std::endl;
sharePtr.reset(); // sharePtr 现在为空指针,原来的对象被释放

(2)std::shared_ptr<void>

泛型数据结构或者函数都可以被std::shared_ptr接管,并且在离开其作用域,会自动调用其析构函数。常见的使用方式:

#include <iostream>
#include <memory>
#include <functional>// 定义任务接口
using Task = std::function<void()>;// 任务执行函数
void executeTask(const std::shared_ptr<Task>& taskPtr) {if (taskPtr) {(*taskPtr)();} else {std::cerr << "Invalid task!\n";}
}int main() {// 创建一个任务并封装在std::shared_ptr中auto taskPtr = std::make_shared<Task>([]() {std::cout << "Task executed!\n";}); // 执行任务executeTask(taskPtr);return 0;
}

(3)类成员智能指针

请注意类成员智能指针是在类外进行初始化,在类内部仅声明了,在构造函数也没有任何显示;

示例代码:

#include <iostream>
#include <memory>class Resource {
public:Resource(int id) : mId(id) {std::cout << "Resource " << mId << " created!" << std::endl;}~Resource() {std::cout << "Resource " << mId << " destroyed!" << std::endl;}void doSomething() {std::cout << "Resource " << mId << " doing something..." << std::endl;}private:int mId;
};class Owner {
public:std::shared_ptr<Resource> mResource;Owner() {std::cout << "Owner created!" << std::endl;}~Owner() {std::cout << "Owner destroyed!" << std::endl;}void useResource() {if (mResource) {mResource->doSomething();} else {std::cout << "No resource available!" << std::endl;}}
};int main() {// 创建 Resource 对象并初始化 shared_ptrstd::shared_ptr<Resource> resource = std::make_shared<Resource>(1);// 创建 Owner 对象并将 resource 分配给 mResourceOwner owner;owner.mResource = resource;// 使用 Owner 对象拥有的 Resource 对象owner.useResource();// Owner 对象在 main() 函数结束时被销毁,同时 Resource 对象也会被正确地释放return 0;
}
3.3.3 weak_ptr

weak_ptr在指向一个对象的时候不会增加其引用计数,weak_ptr 除了内置函数,主要应用于解决shared_ptr 循环使用

(1)weak_ptr

与shared_ptr类似,weak_ptr用法主要是:lock(),expired();lock()函数类似shared_ptr的get函数,expired()类似于use_count()==0

(2)weak_ptr 无法直接访问资源,使用lock方法才能访问资源

(3)防止循环引用,weak_ptr 主要用于解决shared_ptr 循环使用

示例:

class CTxxx {
public:    CTxxx() {printf( "CTxxx cst\n" );}~CTxxx() {printf( "CTxxx dst\n" );};
};int main() {std::shared_ptr<CTxxx> sp_ct(new CTxxx);std::weak_ptr<CTxxx> wk_ct = sp_ct;std::weak_ptr<CTxxx> wka1;{std::cout << "wk_ct.expired()=" << wk_ct.expired() << std::endl;std::shared_ptr<CTxxx> tmpP = wk_ct.lock();if (tmpP) {std::cout << "tmpP usecount=" << tmpP.use_count() << std::endl;} else {std::cout << "tmpP invalid" << std::endl;}std::shared_ptr<CTxxx> a1(new CTxxx);wka1 = (a1);}std::cout << "wka1.expired()=" << wka1.expired() << std::endl;std::cout << "wka1.lock()=" << wka1.lock() << std::endl;std::shared_ptr<CTxxx> cpySp = wka1.lock();if (cpySp) std::cout << "cpySp is ok" << std::endl;else std::cout << "cpySp is destroyed" << std::endl;return 1;
}

(2)weak_ptr循环引用

请注意强弱智能指针的一个重要应用规则:定义对象时,用强智能指针shared_ptr,在其它地方引用对象时,使用弱智能指针weak_ptr

class B; // 前置声明类B
class A
{
public:A() { cout << "A()" << endl; }~A() { cout << "~A()" << endl; }weak_ptr<B> _ptrb; // 指向B对象的弱智能指针。引用对象时,用弱智能指针
};
class B
{
public:B() { cout << "B()" << endl; }~B() { cout << "~B()" << endl; }weak_ptr<A> _ptra; // 指向A对象的弱智能指针。引用对象时,用弱智能指针
};
int main()
{// 定义对象时,用强智能指针shared_ptr<A> ptra(new A());// ptra指向A对象,A的引用计数为1shared_ptr<B> ptrb(new B());// ptrb指向B对象,B的引用计数为1// A对象的成员变量_ptrb也指向B对象,B的引用计数为1,因为是弱智能指针,引用计数没有改变ptra->_ptrb = ptrb;// B对象的成员变量_ptra也指向A对象,A的引用计数为1,因为是弱智能指针,引用计数没有改变ptrb->_ptra = ptra;cout << ptra.use_count() << endl; // 打印结果:1cout << ptrb.use_count() << endl; // 打印结果:1/*出main函数作用域,ptra和ptrb两个局部对象析构,分别给A对象和B对象的引用计数从1减到0,达到释放A和B的条件,因此new出来的A和B对象被析构掉,解决了“强智能指针的交叉引用(循环引用)问题”*/return 0;
}

(3)weak_ptr弱回调

​ 弱回调的作用是实现了一种在事件处理器对象销毁后不再调用回调函数的机制。

​ 具体来说,假设有一个事件处理器对象 handler,它持有一个回调函数对象的弱指针 mCallback。当事件发生时,事件处理器会调用 triggerEvent() 函数,该函数会尝试将弱指针转换为 std::shared_ptr,如果成功则说明回调函数对象仍然存在,可以调用;如果失败则说明回调函数对象已经被销毁,无法调用。

#include <iostream>
#include <memory>
#include <functional>// 定义一个事件处理器类,它持有一个弱指针
class EventHandler {
public:EventHandler() {}void setCallback(std::weak_ptr<std::function<void()>> callback) {mCallback = callback;}void triggerEvent() {// 检查弱指针是否已经过期if (auto callback = mCallback.lock()) {(*callback)();} else {std::cout << "Callback is expired!" << std::endl;}}private:std::weak_ptr<std::function<void()>> mCallback;
};int main() {// 创建一个事件处理器对象EventHandler handler;// 创建一个 shared_ptr,用于保存回调函数auto callback = std::make_shared<std::function<void()>>([]() {std::cout << "Event triggered!" << std::endl;});// 将回调函数传递给事件处理器handler.setCallback(callback);// 触发事件handler.triggerEvent();// 释放回调函数callback.reset();// 再次触发事件,此时因为回调函数已经被释放,会输出 "Callback is expired!"handler.triggerEvent();return 0;
}
3.3.4 unique_ptr

unique_ptr的特点是: (1)管理的资源只能有一个,不能进行拷贝,只能进行移动。(2)轻量:没有引用计数;unique_ptr 常用于vector容器组合使用,用于互斥锁之中;

**(1)unique_ptr **

​ unique_ptr 和share_ptr内置函数类似:reset,get,move ,创建函数std::make_unique

#include <iostream>
#include <memory>int main() {//创建std::unique_ptr<int> ptr1 = std::make_unique<int>(42);std::unique_ptr<int> ptr2(new int(42));//转移所有权std::unique_ptr<int> ptr3 = std::move(ptr1); // ptr1 失去了对资源的所有权//获取原始指针int* rawPtr = ptr.get();ptr.reset(); // 释放所有权并删除对象
}

**(2)std::vector<std::unique_ptr> **

通常情况下,需要把类放入一个容器中,常采用std::unique_ptrstd::vector 结合使用;

示例代码:

#include <iostream>
#include <memory>
#include <vector>class MyClass {
public:MyClass(int data) : mData(data) {std::cout << "MyClass Constructor, Data: " << mData << std::endl;}~MyClass() {std::cout << "MyClass Destructor, Data: " << mData << std::endl;}void doSomething() {std::cout << "MyClass doing something with data: " << mData << std::endl;}
private:int mData;
};int main() {std::vector<std::unique_ptr<MyClass>> vec;// 向 vector 中添加元素vec.push_back(std::make_unique<MyClass>(1));vec.push_back(std::make_unique<MyClass>(2));vec.push_back(std::make_unique<MyClass>(3));// 访问 vector 中的元素for (const auto& ptr : vec) {ptr->doSomething();}// vector 结束生命周期时,所有元素的内存会自动释放return 0;
}

示例2:

#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>class Resource {
public:Resource(int data) : mData(data) {}void doSomething() {std::cout << "Resource " << mData << " is being used." << std::endl;}private:int mData;
};class ResourceManager {
public:void addResource(int data) {std::lock_guard<std::mutex> lock(mMutex);mResources.push_back(std::make_unique<Resource>(data));}void useResources() {std::lock_guard<std::mutex> lock(mMutex);for (const auto& resource : mResources) {resource->doSomething();}}private:std::mutex mMutex;std::vector<std::unique_ptr<Resource>> mResources;
};int main() {ResourceManager manager;// 创建两个线程分别添加资源和使用资源std::thread addThread([&]() {for (int i = 0; i < 5; ++i) {manager.addResource(i);std::this_thread::sleep_for(std::chrono::milliseconds(100));}});std::thread useThread([&]() {for (int i = 0; i < 5; ++i) {manager.useResources();std::this_thread::sleep_for(std::chrono::milliseconds(200));}});addThread.join();useThread.join();return 0;
}
3.3.5 智能指针转换

static_pointer_cast

作用:改函数主要是将继承类的父类指针转换成子类指针;

示例代码:

#include <iostream>
#include <memory>struct BaseClass {};struct DerivedClass : BaseClass {void f() const {std::cout << "Sample word!\n";}
};int main() {std::shared_ptr<BaseClass> ptr_to_base(std::make_shared<DerivedClass>());std::static_pointer_cast<DerivedClass>(ptr_to_base)->f();static_cast<DerivedClass*>(ptr_to_base.get())->f();}

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

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

相关文章

springboot+vue实现oss文件存储

前提oss准备工作 进入阿里云官网&#xff1a;阿里云oss官网 注册 搜OSS&#xff0c;点击“对象存储OSS” 第一次进入需要开通&#xff0c;直接点击立即开通&#xff0c;到右上角AccessKey管理中创建AccessKey&#xff0c;并且记住自己的accessKeyId和accessKeySecret&#…

【Unity】构建简单实用的年份选择器(简单原理示范)

在许多应用程序和游戏中&#xff0c;年份选择是一个常见的需求。无论是在日历应用程序中查看事件&#xff0c;还是在历史类游戏中选择时间段&#xff0c;年份选择器都是用户体验的重要组成部分&#xff0c;下面实现一个简易的年份选择器。 一、效果预览&#xff1a; 目录 一、…

python读写查询mysql数据库

我的免费云服务器&#xff0c;阿贝云数据库操作方法 一、pymysql简介pymysql是一个纯Python编写的MySQL客户端库&#xff0c;用于连接和操作MySQL数据库。它实现了Python DB API v2.0规范&#xff0c;提供了与MySQL服务器进行通信所需的所有基本功能。通过pymysql&#xff0c;开…

多模态3D目标检测-自动驾驶

【ECCV2022】|动态快读的多模态3D目标检测框架 | AutoAlignV2: Deformable Feature Aggregation for Dynamic Multi-Modal 3D Object Detection|论文链接|代码链接 【ECCV2022】|同质多模态数据融合和交互用于3D目标检测 | Homogeneous Multi-modal Feature Fusion and Interac…

树莓派驱动编译

驱动编译前提&#xff1a;驱动代码的编译需要提前编译号的内核 驱动&#xff08;3种实现方法&#xff0c;2条路线&#xff09;_驱动编写三种方法-CSDN博客 驱动的编写_驱动编写-CSDN博客 一、概念 1.1、驱动认识 1、裸机程序中是直接操控硬件的&#xff0c;操作系统…

力扣-跳跃游戏

问题 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 解答 class Solu…

MySQL数据库基础知识总结(适合小白入门使用)一

文章目录 一 数据库数据表的创建等基本操作二 数据类型的测试三 完整性约束条件四 数据表结构的相关操作五 对表中数据的操作六 表达式与查询七 高级的查询功能 一 数据库数据表的创建等基本操作 #注释内容&#xff08;与python很像&#xff09; -- 也为注释内容 -- 创建一个数…

vue3中watch和watchEffect的区别!!!

vue3中watch和watchEffect的区别&#xff01;&#xff01;&#xff01; 在 Vue 3 中&#xff0c;watch 和 watchEffect 都是监听器&#xff0c;但在写法和使用上有所区别。让我们来详细了解一下它们之间的不同&#xff1a; watch: watch 具有一定的惰性&#xff08;lazy&#…

C++ 入门(八)— 常量和字符串

常量和字符串 常量变量常量表达式编译时优化 Constexpr 变量std::string字符串输出 std::coutstd::string可以处理不同长度的字符串字符串输入 std::cin用于输入文本std::getline()不要按值传递Constexpr 字符串 std::string_view可以使用许多不同类型的字符串进行初始化可以接…

v69.字符

1.字符类型 1.1 可以将char类型的变量赋值为整数&#xff0c;也可以赋值为字符! 注意字符要用单引号 ’ ’ 而不是双引号 每一个字符在计算机内部都有一个值去表达它。字符’1’ 在计算机里表示的十进制的整数值为49&#xff0c;就像’A’表示十进制值65。 1.2 scanf 与 p…

C++面试宝典第33题:数组组成最大数

题目 给定一组非负整数nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。 示例1: 输入:nums = [10, 2] 输出:"210" 示例2: 输入:nums = [3, 30, 34, 5, 9] 输出:"…

Flink:流上的“不确定性”(Non-Determinism)

1. 什么是“确定性” 先明确一下什么叫“确定性”&#xff1a;对于一个“操作”来说&#xff0c;如果每次给它的“输入”不变&#xff0c;操作输出的“结果”也不变&#xff0c;那么这个操作就是“确定性“的。通常&#xff0c;我们认为批处理的操作都是确定的&#xff0c;比如…

【查漏补缺你的Vue基础】Vue数据监听深度解析

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【rust】10 project、crate、mod、pub、use、项目目录层级组织、概念和实战

文章目录 一、项目目录层级组织概念1.1 cargo new 创建同名 的 Project 和 crate1.2 多 crate 的 package1.3 mod 模块1.3.1 创建嵌套 mod1.3.2 mod 树1.3.3 用路径引用 mod1.3.3.1 使用绝对还是相对? 1.3.4 代码可见性1.3.4.1 pub 关键字1.3.4.2 用 super 引用 mod1.3.4.3 用…

阿里Java开发手册(黄山版) LeetCode刷题手册 免费下载

目录 一、阿里Java开发手册(黄山版) 二、LeetCode刷题手册 三、获取方式 今天给大家推荐两个程序员的辅助利器&#xff01;都是平时开发&#xff0c;刷算法能经常用到的书籍&#xff0c;怕百度云分享会失效&#xff0c;获取方式在最下面&#xff0c;永久有效。 一、阿里Jav…

C++ 反向迭代器的设计与实现

在本文开始之前&#xff0c;先明晰几个 关键词 的含义&#xff08;T : 模板参数&#xff09;&#xff1a; Ref : T& / const T&Ptr : T* / const T* 一、反向迭代器设计的上帝视角 我们希望将 反向迭代器 设计成一种适配器——传 list::iterator 得到 list 的反向迭代…

嵌入式学习day28 Linux

1.进程间的通信: 1.管道 2.信号 3.消息队列 4.共享内存 5.信号灯 6.套接字 1.管道: 1.无名管道 无名管道只能用于具有亲缘关系的进程间通信 pipe int pipe(int pipefd[2]); 功能: 创建一个无名管道 …

Lastools工具使用

Lastools工具使用 1、介绍 官网链接 常用的功能 las2las可以进行点云拼接las2txt可以将点云中的有效信息转换为txt文档&#xff08;如xyz坐标&#xff09;lasmerge可以将多个LAS/LAZ文件合并为一个文件Laszip将LAS文件高速压缩到LAZ而不会丢失信息las2text将LAS/LAZ转换为可…

数据库事务问题整理-MySQL

什么是数据库事务&#xff1f; 数据库事务( transaction)是访问并可能操作&#xff08;增删改查都可能有&#xff09;各种数据项的一个数据库操作序列&#xff08;可能有1或多个SQL语句&#xff09;&#xff0c;这些操作要么全部执行,要么全部不执行&#xff0c;是一个不可分割…

C++初阶:模版相关知识的进阶内容(非类型模板参数、类模板的特化、模板的分离编译)

结束了常用容器的介绍&#xff0c;今天继续模版内容的讲解&#xff1a; 文章目录 1.非类型模版参数2.模板的特化2.1模版特化引入和概念2.2函数模版特化2.3类模板特化2.3.1全特化2.3.1偏特化 3. 模板分离编译3.1分离编译概念3.2**模板的分离编译**分析原因 1.非类型模版参数 模板…