news 2026/4/17 15:17:22

C++ 智能指针(末):new vs unique_ptr 终极对比 + “指针成员到底是不是创建对象”一次讲透

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 智能指针(末):new vs unique_ptr 终极对比 + “指针成员到底是不是创建对象”一次讲透

目录

一、前言

二、结论先行

三、传统 new/delete 写法(下篇问题源头)

3.1 表面上没问题(“工程幻觉”)

3.2 但工程上隐含 4 个风险点

❌ 风险 1:必须人工维护 new ↔ delete 对称性

❌ 风险 2:多分支/异常路径不可控

❌ 风险 3:默认拷贝 = 双重 delete(经典炸点)

❌ 风险 4:插件卸载/多线程回调极易 UAF

四、unique_ptr 写法(中篇思想的工程落地)

五、逐点对比:unique_ptr 相比 new 到底改变了什么?

对比 1:释放责任从“人”转移给“语言机制”

对比 2:异常/return 安全是天然的

对比 3:拷贝风险被编译期封死

对比 4:所有权规则“自解释”

六、最常见误区:ControlCore* ctrl_core_; 和 std::unique_ptr ctrl_core_; 到底是不是在创建对象?

6.1 ControlCore* ctrl_core_; 的含义

1)含义:

2)总结:

6.2 std::unique_ptr ctrl_core_; 的含义

1)含义:

2)总结:

七、对照表

八、总结


一、前言

承接系列前文:

  • 上篇:裸指针为什么危险(泄漏/异常/多分支/悬空指针)

  • 中篇:RAII 的思想:释放必须绑定生命周期

  • 下篇:车辆运动控制工程实战:unique_ptr / shared_ptr / weak_ptr 在 ROS 中如何落地

这一篇作为末篇,只做两件事:
1)用最小代码把new vs unique_ptr 的工程差异讲到“不可反驳”
2)把最常见误区讲清:ControlCore* ctrl_core_;/unique_ptr<...> ctrl_core_;并不是创建对象


二、结论先行

现代 C++ 不是“不用 new”,
而是“不让你再靠 new/delete 去表达所有权与生命周期”。


三、传统new/delete写法(下篇问题源头)

class VehicleController { public: VehicleController() { ctrl_core_ = new ControlCore(); } ~VehicleController() { delete ctrl_core_; } private: struct ControlCore { void step(double ref, double cur) { (void)ref; (void)cur; } }; ControlCore* ctrl_core_; };

3.1 表面上没问题(“工程幻觉”)

  • 构造函数new

  • 析构函数delete

  • 看起来“对称”


3.2 但工程上隐含 4 个风险点

❌ 风险 1:必须人工维护new ↔ delete对称性

这是一条人为约定,不是语言保证。后续改代码很容易漏。

❌ 风险 2:多分支/异常路径不可控

VehicleController() { ctrl_core_ = new ControlCore(); if (init_failed) return; // delete 走不到 }

❌ 风险 3:默认拷贝 = 双重 delete(经典炸点)

VehicleController a; VehicleController b = a; // 默认拷贝构造 // 两个 ctrl_core_ 指向同一对象 -> 析构 delete 两次 -> 未定义行为

❌ 风险 4:插件卸载/多线程回调极易 UAF

控制器析构了,回调还在用这个裸指针,就会 Use-After-Free。


四、unique_ptr写法(中篇思想的工程落地)

class VehicleController { public: VehicleController() { ctrl_core_ = std::make_unique<ControlCore>(); } private: struct ControlCore { void step(double ref, double cur) { (void)ref; (void)cur; } }; std::unique_ptr<ControlCore> ctrl_core_; };
名称属于哪一层标准叫法是不是对象具体含义
ControlCore类型层类型 / 结构体类型❌ 否定义了一种“控制核心”的蓝图,描述它长什么样
VehicleController类型层类类型❌ 否定义了一种“车辆控制器”的蓝图
VehicleController()成员函数构造函数❌ 否控制器对象“出生时”执行的初始化逻辑
ctrl_core_对象成员成员变量❌(本身不是 ControlCore 对象)用来持有/管理某个ControlCore对象
std::unique_ptr<ControlCore>类型层智能指针类型❌ 否表达“对 ControlCore 的唯一所有权”的类型
std::make_unique<ControlCore>()表达式对象创建语句✅ 是在堆上创建一个ControlCore对象
ControlCore 对象运行时实体对象实例✅ 是真正参与控制计算的那个“实体”

五、逐点对比:unique_ptr 相比 new 到底改变了什么?

对比 1:释放责任从“人”转移给“语言机制”

写法谁负责释放
new/delete人(靠记忆、靠规范)
unique_ptrC++ 生命周期规则(成员析构自动释放)

对比 2:异常/return 安全是天然的

构造中途throw/return,不会泄漏;裸指针要靠人补齐每条路径。

对比 3:拷贝风险被编译期封死

VehicleController a; VehicleController b = a; // ❌ 编译期报错(unique_ptr 不可拷贝)

对比 4:所有权规则“自解释”

std::unique_ptr<ControlCore> ctrl_core_;

看到就知道:唯一拥有、不可共享、生命周期绑定。


六、最常见误区:ControlCore* ctrl_core_;std::unique_ptr<ControlCore> ctrl_core_;到底是不是在创建对象?

很多人会误以为下面两行是在“创建 ControlCore 对象”:

ControlCore* ctrl_core_; std::unique_ptr<ControlCore> ctrl_core_;

但它们都不是创建ControlCore对象,它们做的事情是:

VehicleController这个类里声明一个成员变量

  • 裸指针:只是“地址槽位”

  • unique_ptr:是“带唯一所有权语义的管理器槽位”

用来保存(或管理)某个ControlCore对象。

真正“创建对象”的动作,发生在new/make_unique那一行,而不是这两行。

6.1 ControlCore* ctrl_core_;的含义

ControlCore* ctrl_core_;

1)含义:

  • 声明一个“裸指针成员变量”

  • 这个变量里能放一个地址(ControlCore*

  • 通过这个地址可以访问某个ControlCore对象

注意:它不负责创建对象,也不负责释放对象。

也就是说,这行只是:

“我准备留一个地方,未来可以存放一个ControlCore的地址。”

对象一般是后面才创建并赋值的,例如:

ctrl_core_ = new ControlCore(); // ✅ 这行才创建对象(堆上)

如果你创建了对象,最终还得手动释放:

delete ctrl_core_; ctrl_core_ = nullptr;

2)总结:

ControlCore* ctrl_core_;= “我有个地址槽位,但谁拥有对象、谁负责释放完全没写在类型里。”


6.2std::unique_ptr<ControlCore> ctrl_core_;的含义

std::unique_ptr<ControlCore> ctrl_core_;

1)含义:

  • 声明一个“独占型智能指针成员变量”

  • 它内部同样存着一个ControlCore*地址

  • 但它额外表达并强制一条规则:

如果它指向了一个ControlCore对象,那么它就是该对象的唯一所有者(owner),并负责在析构时自动释放。

注意:这行本身也不创建ControlCore对象,只是声明一个“管理器变量”。

对象仍然需要你在构造函数里创建,例如:

ctrl_core_ = std::make_unique<ControlCore>(); // ✅ 这行才创建对象(堆上)

不同的是:你不需要写 delete,因为当VehicleController析构时:

  • ctrl_core_成员析构

  • unique_ptr析构会自动delete它管理的对象

并且unique_ptr有额外的工程保证:

  • 不能拷贝(避免双重 delete)

  • 只能 move(显式转移所有权)

2)总结:

std::unique_ptr<ControlCore> ctrl_core_;= “我有个专属负责人槽位:只要对象归我管,我就负责它的生死。”


七、对照表

代码是否创建对象是否表达所有权是否自动释放
ControlCore* ctrl_core_;(声明成员变量)

std::unique_ptr<ControlCore> ctrl_core_;

(声明成员变量)

✅(唯一)
ctrl_core_ = new ControlCore();❌(仍不明确)
ctrl_core_ = std::make_unique<ControlCore>();

八、总结

new负责“造对象”,但它不负责“谁来管对象”。
unique_ptr不只是“自动 delete”,它是在类型层面写死:
对象的唯一拥有者是谁,生命周期跟谁绑定,能不能拷贝,什么时候必然释放。

所以在车辆运动控制工程里:
如果对象应该与控制器同生共死,用unique_ptr不是习惯,而是工程正确性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/30 22:25:30

用JasperSoft Studio快速验证数据可视化创意

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个快速原型生成器&#xff0c;能够&#xff1a;1) 接受CSV/Excel数据输入&#xff1b;2) 自动生成多种可视化方案&#xff1b;3) 支持一键样式切换&#xff1b;4) 导出可分享…

作者头像 李华
网站建设 2026/4/18 2:37:38

[内网流媒体] 服务异常后的自动恢复策略

背景 实时画面服务长期运行中难免遇到崩溃、编码失败、端口被占用等异常。缺少自动恢复会导致长时间不可用。本文总结内网场景下的自动恢复策略与实现建议。 常见异常类型 采集失败:设备断开、权限被收回、屏幕锁定。 编码失败:硬编初始化失败、软编线程异常退出。 端口占…

作者头像 李华
网站建设 2026/4/18 7:36:03

从传统BI到Apache Superset:效率提升300%的转型之路

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个效率对比演示项目&#xff0c;展示Apache Superset相比传统BI工具的优势&#xff1a;1) 搭建一个完整数据分析平台的耗时对比&#xff1b;2) 相同查询在不同平台下的响应时…

作者头像 李华
网站建设 2026/4/18 7:26:26

JavaScript堆内存溢出:新手必看指南

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个面向初学者的教程&#xff0c;解释JavaScript内存管理基础知识。包括&#xff1a;什么是堆内存、为什么会溢出、如何查看内存使用情况&#xff08;process.memoryUsage()&…

作者头像 李华
网站建设 2026/4/17 23:13:44

AI如何帮你自动配置JDK环境变量?

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个智能工具&#xff0c;能够自动检测用户的操作系统&#xff08;Windows/macOS/Linux&#xff09;&#xff0c;并根据系统版本自动下载、安装和配置最新版本的JDK。工具应包…

作者头像 李华
网站建设 2026/4/18 8:28:00

游戏服务器运维:解决PresenceWriter错误的5个实战案例

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个Windows游戏服务器错误案例库应用&#xff0c;专门收集Gaming.GameBar.PresenceServer.Internal.PresenceWriter相关错误案例。每个案例包含错误现象、环境信息、诊断过程…

作者头像 李华