首页IT科技c++ 智能指针unique_ptr(c++ 智能指针总结)

c++ 智能指针unique_ptr(c++ 智能指针总结)

时间2025-05-06 13:44:00分类IT科技浏览3533
导读:内存管理 堆...

内存管理

定义:动态分配内存的区域

c++会设计到的两个有关内存管理器的操作

让内存管理器分配一个某大小的内存块

分配内存要考虑程序当前已经有多少未分配的内存

内存不足时要从操作系统申请新的内存;内存充足时            ,从可用内存里取出一块合适大小的内存                  ,将其标记为已用      ,再将其返回给要求内存的代码

让内存管理器释放一个之前分配过的内存块

对于连续未使用的内存块         ,内存管理器需要将其合并成一块                  ,以便可以满足后续较大内存的分配

定义:函数调用过程中产生的本地变量和调用数据的区域 大致执行过程: 当函数调用另一个函数时         ,会把参数也压入栈里      ,然后把下一行汇编指令的地址压入栈                  ,并跳转到新的函数            。新的函数进入后            ,首先做一些必须的保存工作   ,然后会调整栈指针                  ,分配出本地变量所需的空间               ,随后执行函数中的代码,并在执行完毕之后               ,根据调用者压入栈的地址                  ,返回到调用者未执行的代码中继续执行 优点: 栈上的分配十分简单   ,只需移动栈指针 栈上的释放十分简单            ,函数执行结束时只需移动栈指针 由于后进先出的执行过程                  ,不可能出现内存碎片

RAII(Resource Acquisition Is Initialization)

定义:RAII 依赖于栈和析构函数      ,对所有的资源进行管理 栈方便且风险低         ,为什么使用RAII? 对象占用内存很大 在编译期不能确定对象的大小 对象是函数的返回值                  ,但由于特殊原因(如对象切片)         ,不应使用对象的值进行返回 用途: 在析构函数里释放内存 关闭文件 释放同步锁 释放其他重要的系统资源

智能指针

什么是智能指针? 完全实践RAII      ,包装裸指针 行为类似常规指针                  ,但负责自动释放所指对象 一个模板 使用动态内存 为什么使用智能指针? 能够自动适应各种复杂的情况            ,防止误用指针导致的隐患                  。如   ,内存泄漏 三种智能指针      。都定义在<memory> unique_ptr         。独占所指对象 shared_ptr                  。允许多个指针指向同一对象 weak_ptr         。一种弱引用                  ,指向shared_ptr所管理的对象 建议 内置指针仅用于范围            、循环或助手函数有限的小代码块中               ,这些代码块的性能非常关键,而且不存在混淆所有权的可能性 在单独的代码行上创建智能指针               ,而不要在参数列表中创建智能指针                  ,这样就不会因为参数列表分配规则而发生可能的资源泄漏

unique_ptr

操作

unique_ptr u1

unique_ptru2 空unique_ptr,可以指向类型为T的对象      。

u1自己调用delete释放它的指针;u2自己调用一个类型为D的可调用对象来释放它的指针 unique_ptr u(d) 空unique_ptr   ,指向类型为T的对象            ,用类型为D的对象d代替delete u = nullptr 释放u指向的对象                  ,将u置空 u.release() 返回指针      ,u放弃对指针的控制权         ,并将u置空 u.reset() 释放u指向的对象 u.reset(q)

u.reset(nullptr) 如果提供内置指针q                  ,令u指向q指向的对象                  。否则将u置空

其中         ,T是对象类型      ,D为删除器

更多的参见shared_ptr

特性:

一个unique_ptr只能指向一个对象                  ,且当unique_ptr被销毁时            ,它所指向的对象也被销毁 unique_ptr离开作⽤域时   ,若其指向对象                  ,则将其所指对象销毁(默认delete) unique_ptr不是指针               ,而是对象 定义unique_ptr时,需要将其绑定到一个new返回的指针 不能对unique_ptr调用delete            。它会自动管理初始化时的指针               ,在离开作用域时析构释放内存 不支持加减运算                  ,不能随意移动指针地址 不支持普通的拷贝或赋值

所有权

一个unique_ptr只能指向一个对象   ,且当unique_ptr被销毁时            ,它所指向的对象也被销毁   。为了实现这个目的                  ,unique_ptr使用了move语义      ,且禁止拷贝赋值         ,必须使用std::move()显示地声明所有权转移 尽量不要对 unique_ptr 执行赋值操作

shared_ptr

操作

shared_ptr和unique_ptr都支持的操作 shared_ptr sp

unique_ptr up 空智能指针                  ,可以指向类型为T的对象 p 若p指向一个对象         ,则为true *p 解引用p,获得它所指对象 p->mem 等价于(*p).mem p.get() 返回p中保存的指针 swap(p,q)

p.swap(q) 交换p和q中的指针 shared_ptr独有的操作 make_shared(args) 返回一个shared_ptr,指向动态分配的类型为T的对象

使用args初始化此对象 shared_ptrp(q) p是shared_ptr q的拷贝

递增q的引用计数

q中的指针必须可以转换为T* p = q p 和 q都是shared_ptr      ,所保存的指针必须可以相互转换

递减p的引用计数                  ,递增q的引用计数 p.use_count() 返回与p共享对象的智能指针数量

比较慢            ,用于调试 p.unique() 若p.use_count() 为1   ,返回true                  ,否则false 定义和改变shared_ptr shared_ptr p(q) p管理内置指针q指向的对象               ,且q必须指向new分配的内存,还能转换为T*类型 shared_ptr p(u) p从unique_ptr u接管对象的所有权

将u置空 shared_ptr p(q,d) p接管内置指针q指向的对象的所有权,q必须能转换为T*类型

p使用可调用对象d代替delete shared_ptr p(p2,d) p是shared_ptr p2的拷贝               ,p使用可调用对象d代替delete p.reset()

p.reset(q)

p.reset(q,d) 若p是唯一指向对象的shared_ptr,reset会释放p的对象

若传递内置指针q                  ,则令p指向q   ,否则将p置空

若还传递d            ,使用可调用对象d代替delete

所有权

与unique_ptr的所有权不同                  ,shared_ptr的所有权可以被安全共享                  。因为它的内部使用引用计数

特性

当引用计数减少到 0      ,shared_ptr会自动调用 delete 释放内存 shared_ptr的实现机制是在拷⻉构造时使⽤同⼀份引⽤计数 当对象不再被使用         ,shared_ptr会自动调用 delete 释放内存 shared_ptr可以在任何场合替代内置指针                  ,而不用担心资源回收的问题

注意事项

引用计数的存储和管理都是成本         ,不如unique_ptr

引用计数的变动非常复杂      ,很难知道其真正释放资源的时机               。因此                  ,对象的析构函数不要有非常复杂                  、严重阻塞的操作

无法解决循环引用问题

不建议在函数的参数使用shared_ptr            ,因为成本高

同⼀个shared_ptr被多个线程“读            ”是安全的

同⼀个shared_ptr被多个线程“写                  ”是不安全的

在多个线程中同时对⼀个shared_ptr循环执⾏两遍swap。 shared_ptr的swap函数的作⽤就是和另外⼀个shared_ptr交换引⽤对象和引⽤计数   ,是写操作               。执⾏两遍swap之后, shared_ptr引⽤的对象的值应该不变

共享引⽤计数的不同的shared_ptr被多个线程      ”写“ 是安全的

weak_ptr

操作

weak_ptr weak_ptr w 空weak_ptr可以指向类型为T的对象 weak_ptr w(sp) w与shared_ptr sp指向相同对象

T必须能转换为sp指向的类型 w = p p可以是shared_ptr/weak_ptr

赋值后w 和 p共享对象 w.reset() 将w置空 w.use_count() 与w共享对象的shared_ptr的数量 w.expired() 若w.use_count 为0,返回true,否则false w.lock() 若expired为true                  ,返回一个空的shared_ptr

否则返回一个指向w的对象的shared_ptr

什么是wake_ptr?

wake_ptr是一种不控制所指对象生存期的智能指针               ,指向一个由shared_ptr管理的对象

为什么使用wake_ptr?

解决shared_ptr存在的循环引用问题

循环引用:

class Node final { public: using this_type= Node; using shared_type = std::shared_ptr<this_type>; public: shared_type next; // 使用智能指针来指向下一个节点 }; auto n1 = make_shared<Node>(); // 工厂函数创建智能指针 auto n2 = make_shared<Node>(); // 工厂函数创建智能指针 assert(n1.use_count() == 1);// 引用计数为1 assert(n2.use_count() == 1); n1->next = n2; // 两个节点互指,形成了循环引用 n2->next = n1; assert(n1.use_count() == 2); // 引用计数为2 assert(n2.use_count() == 2); // 无法减到0               ,无法销毁                  ,导致内存泄漏

解决:

将shared_ptr改成weak_ptr

class Node final { public: using this_type= Node; using shared_type = std::weak_ptr<this_type>; public: shared_type next; // 使用智能指针来指向下一个节点 }; //... if (!n1->next.expired()) // 检查指针是否有效 { auto ptr = n1->next.lock(); // lock()获取shared_ptr assert(ptr == n2); }

注意事项

将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数 一旦最后一个指向对象的shared_ptr被销毁   ,对象就会被释放            ,即使weak_ptr指向对象 创建一个weak_ptr时                  ,需要用shared_ptr初始化 由于对象可能不存在      ,不能使用weak_ptr直接访问对象         ,而是调用lock()

reference

Smart pointers (Modern C++) | Microsoft Learn

罗剑锋的 C++ 实战笔记 (geekbang.org)

C++ Primer 中文版(第 5 版) (豆瓣) (douban.com)

知识星球 | 深度连接铁杆粉丝                  ,运营高品质社群         ,知识变现的工具 (zsxq.com)

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
python csv writerows(python中csv如何设置表头?)