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

C++智能指针概念理解的面试题

C++智能指针概念理解的面试题

第一部分:基础概念
  1. 解释std::unique_ptrstd::shared_ptr在以下方面的区别:
    • 所有权语义
    • 性能开销
    • 自定义删除器的存储方式
    • 是否支持数组类型

    答案:

    所有权语义:

    unique_ptr:独占所有权,不能复制,只能移动shared_ptr:共享所有权,通过引用计数管理,可以复制
    

    性能开销:

    unique_ptr:几乎无额外开销(等同于原始指针)shared_ptr:有控制块和引用计数的开销
    

    自定义删除器的存储方式:

    unique_ptr:删除器是类型的一部分,直接存储在对象中(无额外开销)shared_ptr:删除器存储在控制块中(类型擦除,有额外开销)
    

    是否支持数组类型:

    unique_ptr:通过unique_ptr<T[]>显式支持数组shared_ptr:不直接支持数组,需自定义删除器
    
  2. 考虑以下代码:
std::shared_ptr<int> p1(new int(42));
std::shared_ptr<int> p2 = std::make_shared<int>(42);
  • 这两种初始化方式在内存分配上有何不同?
  • 为什么推荐使用make_shared
  • 在什么情况下不能使用make_shared

答案:

内存分配差异

  • shared_ptr<int> p1(new int(42)):两次分配(对象和控制块分开)
  • make_shared<int>(42):一次分配(对象和控制块合并)

推荐make_shared的原因

  1. 更高性能(单次分配)
  2. 更安全(避免内存泄漏)
  3. 更好的缓存局部性

不能使用make_shared的情况

  1. 需要自定义删除器
  2. 需要大括号初始化
  3. 需要weak_ptr长期存在而对象可被释放的场景
第二部分:深入实现
  1. 假设C++标准库中没有提供std::weak_ptr,你如何仅使用std::shared_ptr来实现一个弱引用指针?请描述你的设计方案,包括:
    • 如何检测所指向的对象是否已被释放
    • 如何实现lock()操作来获取可用的shared_ptr
    • 如何避免循环引用

答案:

设计方案

  1. 使用shared_ptr的引用计数结构扩展
  2. 添加"弱引用计数"跟踪观察者数量
  3. 对象释放条件:强引用=0(无论弱引用)

实现lock()

  1. 检查强引用计数>0
  2. 如果对象存在,增加强引用计数并返回新shared_ptr
  3. 否则返回空shared_ptr

避免循环引用

  1. 弱引用不参与对象生命周期管理

  2. 仅当强引用=0时才释放对象

  3. 控制块在弱引用=0时才释放

  4. 解释std::shared_ptr的引用计数机制:
  • 控制块(control block)通常包含哪些信息?
  • 在多线程环境下,引用计数如何保证原子性?
  • 为什么std::shared_ptr的引用计数使用原子操作而不是简单的整数?

答案:

控制块内容

  1. 强引用计数
  2. 弱引用计数
  3. 自定义删除器
  4. 分配器(如使用)
  5. 指向被管理对象的指针

原子性保证

  1. 使用原子操作(如std::atomic)修改引用计数
  2. 内存序保证(通常memory_order_relaxed用于计数)
  3. 控制块本身线程安全

使用原子操作的原因

  1. 多线程环境下安全修改计数

  2. 避免数据竞争

  3. 保证内存可见性

第三部分:高级应用
  1. 考虑以下自定义删除器的使用场景:
void file_deleter(FILE* fp) {std::fclose(fp);
}std::unique_ptr<FILE, decltype(&file_deleter)> fp(std::fopen("test.txt", "r"), file_deleter);
  • 为什么这里decltype(&file_deleter)是必要的?
  • 如果改用lambda表达式作为删除器,代码应该如何修改?
  • 比较std::unique_ptrstd::shared_ptr在自定义删除器存储方式上的差异

答案:

decltype(&file_deleter)必要性

  • unique_ptr的删除器是类型的一部分
  • 必须明确指定删除器类型

lambda删除器

auto deleter = [](FILE* fp) { std::fclose(fp); };
std::unique_ptr<FILE, decltype(deleter)> fp(std::fopen("test.txt", "r"), deleter);

存储方式差异

  • unique_ptr:删除器作为模板参数,直接存储

  • shared_ptr:删除器类型擦除,存储在控制块

  1. 设计一个支持多态对象的安全删除方案:
struct Base { virtual ~Base() = default; };
struct Derived : Base { /*...*/ };std::shared_ptr<Base> p = std::make_shared<Derived>();
  • 解释为什么这个方案是类型安全的
  • 如果Base的析构函数不是虚函数,会发生什么?
  • 如何设计一个工厂函数,返回基类指针但能正确删除派生类对象?

答案:

类型安全原因

  1. make_shared创建完整派生类对象
  2. 虚析构函数确保正确析构顺序
  3. 共享指针保持完整类型信息

非虚析构函数问题

  1. 派生类部分不会被析构
  2. 资源泄漏
  3. 未定义行为

工厂函数设计

template<typename Derived, typename... Args>
std::shared_ptr<Base> create(Args&&... args) {return std::shared_ptr<Base>(new Derived(std::forward<Args>(args)...));
}
第四部分:陷阱与最佳实践
  1. 分析以下代码的问题:
std::shared_ptr<int> create_shared() {int* raw = new int(42);std::shared_ptr<int> p1(raw);std::shared_ptr<int> p2(raw);return p1;
}
  • 这段代码会导致什么问题?
  • 如何修改才能使其安全?
  • 解释为什么std::enable_shared_from_this能解决类似问题

答案:

问题

  1. 两个shared_ptr独立管理同一原始指针
  2. 会导致双重释放
  3. 引用计数不共享

修改方案

std::shared_ptr<int> create_shared() {auto p1 = std::make_shared<int>(42);std::shared_ptr<int> p2 = p1;  // 共享所有权return p1;
}

enable_shared_from_this作用

  1. 允许对象安全地生成指向自身的shared_ptr

  2. 内部使用weak_ptr避免循环引用

  3. 确保所有shared_ptr共享同一控制块

  4. 讨论在以下场景中智能指针的使用策略:

  • 作为类成员变量
  • 在容器中存储动态分配的对象
  • 跨线程传递对象所有权
  • 与第三方C库交互

答案:

类成员变量

  • 优先unique_ptr表达独占
  • 需要共享时用shared_ptr
  • 观察用weak_ptr

容器存储

  • 优先unique_ptr(明确所有权)
  • 需要共享时用shared_ptr
  • 考虑vector<unique_ptr>替代vector<Base*>

跨线程传递

  1. shared_ptr引用计数线程安全
  2. 对象本身需额外同步
  3. 避免跨线程传递原始指针

与C库交互

  1. 自定义删除器包装C接口
  2. unique_ptr管理C资源
  3. 注意所有权转移语义

相关文章:

  • 4/25 研0学习日志
  • 【产品经理】常见的交互说明撰写方法
  • 工业电子测量中的安全隐患与解决方案——差分探头的技术优势解析
  • 小白电路设计-设计11-恒功率充电电路设计
  • 人工智能与机器学习:Python从零实现逻辑回归模型
  • 遥测终端机,推动灌区流量监测向数据驱动跃迁
  • P19:Inception v1算法实战与解析
  • 第1讲|R语言绘图体系总览(Base、ggplot2、ComplexHeatmap等)
  • 2025年GPLT团体程序设计天梯赛L1-L2
  • 电力系统最小惯性常数解析
  • 技术前沿:剖析Atheris在Python项目模糊测试中的核心优势
  • 技术面试一面标准流程
  • 查找函数【C++】
  • Pandas中的日期时间date处理
  • 进程和线程的区别、联系与典型应用场景
  • OpenWrt 与 Docker:打造轻量级容器化应用平台技术分享
  • 【不同名字的yolo的yaml文件名是什么意思】
  • window.location.href的用法
  • 中国矿业大学iGMAS分析中心介绍
  • 天机学堂day10作业,完善兑换优惠券功能
  • 蜀道考古调查阶段性成果发布,新发现文物遗存297处
  • 明查|把太平洋垃圾污染问题甩锅中国,特朗普用的是P过的图
  • “谁羽争锋”全国新闻界羽毛球团体邀请赛在厦门开赛
  • 吏亦有道|秦汉的发明家与技术传承
  • 下周起上海浦东将投放5000万元消费券,预计分五周发放
  • 央媒关注脑瘫女骑手:7年跑出7.3万多份单,努力撑起生活