当前位置: 首页 > news >正文

[C++面试] 智能指针面试点(重点)续3

[C++面试] RAII资源获取即初始化(重点)-CSDN博客

[C++面试] 智能指针面试点(重点)-CSDN博客

[C++面试] 智能指针面试点(重点)续1-CSDN博客

[C++面试] 智能指针面试点(重点)续2-CSDN博客

一、入门

1、unique_ptr

空智能指针创建与资源绑定:

std::unique_ptr<int> u_i;              // 创建空智能指针(未持有任何资源)
u_i.reset(new int(3));                 // 通过 reset() 绑定新资源(值为3的int对象)

reset() 是 unique_ptr 的成员函数,用于释放当前资源并绑定新资源。 空指针初始状态无资源所有权,reset() 会触发原资源释放(若存在)。

构造函数直接初始化:

std::unique_ptr<int> u_i2(new int(4)); // 创建时直接绑定动态分配的int对象(值为4)

// c++ 14之后
auto u_i3 = std::make_unique<int>(5);      // 创建指向int的unique_ptr,值为5
auto u_s3 = std::make_unique<std::string>("def"); // 创建指向string的unique_ptr,值为"def

std::make_unique<T>(args) 是 C++14 引入的工厂函数 

所有权释放与转移:

int *p_i = u_i2.release();            // 释放所有权,返回原生指针(u_i2变为空指针)
std::unique_ptr<std::string> u_s(new std::string("abc"));
std::unique_ptr<std::string> u_s2 = std::move(u_s); // 通过移动语义转移所有权

显式资源释放与重置: 

u_s2.reset(u_s.release());            // 释放u_s资源并由u_s2接管
u_s2 = nullptr;                       // 显式销毁资源并置空(等价于 u_s2.reset())

手动释放原生指针: 

delete p_i;                           // 释放通过 release() 获得的原生指针

“ 调用 release() 后,​资源释放的责任转移到返回的原生指针,需手动管理”。

2、简单介绍一下reset和release

 reset() 在 std::unique_ptr 中,其核心作用是 ​释放当前管理的资源​(若存在),并可以选择绑定新的资源。

step1:若 unique_ptr 当前持有资源(即非空指针),调用 reset() 会立即通过 delete 或自定义删除器释放该资源

step2:若 reset() 接受参数(如 reset(new int(20))),则释放旧资源后,unique_ptr 会接管新资源的所有权

std::unique_ptr<int> u_i2(new int(4));
u_i2.reset(new int(20));  // 释放原资源(int(4)),绑定新资源(int(20))
std::unique_ptr<int> u_i2(new int(4));  // 初始绑定资源(值为4的int对象)
u_i2.reset();                          // 释放资源并置空,不再绑定任何资源

 执行后状态

  • u_i2.get() == nullptr,表示无资源绑定。
  • 原资源 int(4) 已被销毁

shared_ptr 内部维护一个引用计数器(强引用计数),每次拷贝构造或赋值操作时,引用计数自增;每次析构或调用 reset() 时,引用计数自减。当引用计数归零时,自动释放资源。

auto sp1 = std::make_shared<int>(10);  // 引用计数初始化为1
auto sp2 = sp1;                        // 引用计数增至2
sp2.reset();                           // sp2释放资源,引用计数减至1

 释放当前资源并绑定新资源(可选)。无参数时,仅释放资源并置空指针。

std::shared_ptr<int> p1(new int(5));   // 直接构造
auto p2 = std::make_shared<int>(10);   // 推荐方式:避免内存碎片
p1.reset(new int(20));  // 释放原int(5),绑定新int(20)
p1.reset();             // 释放资源,p1变为空指针

注: 尽量通过移动语义传递 shared_ptr,减少引用计数操作


release() 的作用是 ​释放 unique_ptr 对当前资源的所有权,但 ​不释放资源

释放所有权release() 返回当前管理的原生指针(如 int*),并将 unique_ptr 内部指针置为空(变为空指针)

调用 release() 后,​资源释放的责任转移到返回的原生指针,需手动管理

std::unique_ptr<int> u_i2(new int(4));
int *p_i = u_i2.release();  // u_i2 变为空指针,p_i 持有原资源
delete p_i;  // 必须手动释放,否则内存泄漏

原生指针是普通的 C++ 指针(如 int*),它仅存储内存地址,​不负责资源的生命周期管理。 

3、unique_ptr相比于auto_ptr有什么优势?

unique_ptr 通过 ​编译期禁用拷贝构造函数和赋值运算符 彻底解决了 auto_ptr 的所有权隐式转移问题。同样都可能有Bug,但是unique_ptr可以在编译期解决,而auto_ptr要在运行期。

auto_ptr<int> ap1(new int(10));
auto_ptr<int> ap2 = ap1;  // 允许赋值,但 ap1 变为空指针(运行时风险)

unique_ptr<int> up1(new int(10));
unique_ptr<int> up2 = up1;  // 编译报错,禁止拷贝[1,6](@ref)

unique_ptr 通过 std::move 实现 ​显式的所有权转移,代码意图更清晰 

unique_ptr<int> up1(new int(10));
unique_ptr<int> up2 = std::move(up1);  // up1 被置空,所有权转移至 up2
vector<unique_ptr<int>> vec;
vec.push_back(std::move(up1));  // 合法
特性auto_ptr (C++98)unique_ptr (C++11)
所有权转移隐式(运行时风险)显式(std::move,编译安全)
拷贝/赋值允许(导致悬空指针)禁用(编译报错)
数组支持不支持(delete 错误)支持(delete[]
容器兼容性不兼容兼容(通过移动语义)
自定义删除器不支持支持
线程安全性无保障需外部同步(引用计数非线程安全)

4、 使用 unique_ptr 是否需要有效性判断?

  • 所有权转移后:当通过 std::move 或 release() 转移所有权后,原 unique_ptr 会被置空,此时需检查有效性。
  • 从外部接收指针:若不确定指针是否已被释放,应进行验证。
// 法1:
unique_ptr<int> up(new int(10));
if (up) {  // 隐式调用 operator bool()
  cout << *up << endl;  // 有效时操作
}

// 法2:
if (up != nullptr) { /* ... */ }

// 法3:​get() 方法:返回原生指针,若为空则无效
if (up.get() != nullptr) { /* ... */ }

避免手动操作原生指针:除非必要,不要通过 get() 或 release() 暴露内部指针 

原生指针:是普通的 C++ 指针(如 int*),它仅存储内存地址,​不负责资源的生命周期管理。在 release() 返回后,必须通过 delete 或传递给其他智能指针手动释放资源

unique_ptr的一种惯用法

unique_ptr <string> demo(const char * s)
{
    unique_ptr <string> temp (new string (s));
    return temp;
}

unique_ptr <string> ps;
ps = demo("Uniquely special");

二、进阶

1、​weak_ptr为啥取名叫weak?

被称为 "weak"(弱)指针,是因为它不拥有对象的 ​所有权,仅作为观察者存在,不会影响对象的生命周期。相比之下,std::shared_ptr 是强引用指针,直接参与对象所有权的管理。

weak_ptr 不拥有对象的所有权,不会增加对象的 ​强引用计数​(即 shared_ptr 的引用计数)。

是观察者:类似于房屋中介,不拥有房子,但是可以带人看房。它通过观察 shared_ptr 管理的对象,提供一种安全访问的机制。即使对象已被释放,weak_ptr 也能检测到这一状态,避免悬空指针问题

auto sp = std::make_shared<int>(10);  // 强引用计数=1
std::weak_ptr<int> wp(sp);            // 弱引用计数=1,强引用计数仍为1
  • shared_ptr:可直接通过 operator* 或 operator-> 访问对象。
  • weak_ptr:必须通过 lock() 方法转换为 shared_ptr 后才能访问对象。会尝试将 weak_ptr 转换为 std::shared_ptr,从而 ​临时获取资源的强引用。如果原资源已被释放,lock() 会返回空指针,避免访问无效内存
if (auto tmp = wp.lock()) {  // 原子操作,安全检测对象是否存活
    std::cout << *tmp << std::endl;
}

 lock() 是原子操作,保证在多线程中安全检测对象状态。但避免在 lock() 调用期间资源被其他线程释放。

2、weak_ptr不调用lock可以吗?

 不调用 lock() 的后果

  • 直接访问是未定义行为
    weak_ptr ​没有重载 operator* 或 operator->,无法直接解引用或访问对象成员。若强制转换或通过其他方式(如 get())获取原生指针,可能导致悬空指针访问,引发程序崩溃或数据损坏
  • 若通过 weak_ptr 的 get() 方法获取原生指针,即使资源未被释放,也可能因强引用计数归零后随时被销毁,无法保证生命周期。
  • 无法保证资源有效性
    即使通过 expired() 检查资源是否存活,在多线程环境中仍可能出现竞态条件(例如检查后资源被其他线程释放)

虽然 expired() 可以检测资源是否存活,但它仅提供瞬时状态,无法保证后续操作的安全性。因此仍需配合 lock() 使用

if (!wp.expired()) {       // 不可靠的瞬时检查
    auto sp = wp.lock();   // 仍需再次确认
    sp->doSomething();
}

http://www.dtcms.com/a/100029.html

相关文章:

  • 【视觉提示学习】3.28阅读随想
  • 11. STL的使用
  • springBoot统一响应类型3.4版本
  • LlamaIndex实现(基于PDF|CSV文件)RAG检索增强生成:NaiveRAG
  • 量子计算:未来计算技术的革命性突破
  • 【奶茶经济学的符号暴力本质】
  • 【软件开发】可复用的数据库导入工具类
  • J2EE框架技术 第五章 Spring注入与作用域
  • 【C++】STL库_stack_queue 的模拟实现
  • 【leetcode】通过两种遍历方式构造二叉树
  • 前端页面缓存问题
  • opencv之指纹验证
  • spring-security原理与应用系列:securityFilterChainBuilders
  • 职测-言语理解与表达
  • SD-WAN组网方案
  • pycharm相对路径引用方法
  • C++ 多态:面向对象编程的核心概念(一)
  • Android Product Flavors 深度解析与最佳实践:构建多版本应用的全方位指南
  • Redis的补充和RESP
  • 【工具】BioPred一个用于精准医疗中生物标志物分析的 R 软件包
  • C# StreamReader/StreamWriter 使用详解
  • 什么是 RBAC 权限模型?
  • jmeter web压力测试 压测
  • Android学习总结之算法篇三(打家劫舍)
  • 蓝桥杯—最小公倍数
  • Linux系统之dump命令的基本使用
  • Linux系统禁用swap
  • Xenium | 细胞邻域(Cellular Neighborhood)分析(fixed radius)
  • Spring AI MCP Server + Cline 快速搭建一个数据库 ChatBi 助手
  • QML编程中的性能优化二