news 2026/4/15 6:05:08

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/4/15 6:03:52

AI助力快速获取CENTOS8下载资源

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个AI辅助工具&#xff0c;自动识别并推荐CENTOS8的官方下载地址。功能包括&#xff1a;1. 自动检测用户所在地区&#xff0c;推荐最近的镜像站点&#xff1b;2. 验证下载链接…

作者头像 李华
网站建设 2026/4/14 20:25:42

VibeVoice vs 传统TTS:对话级语音合成的技术革新之路

VibeVoice vs 传统TTS&#xff1a;对话级语音合成的技术革新之路 在播客制作间里&#xff0c;一位内容创作者正面对着屏幕发愁——她需要录制一期45分钟的三人对谈节目&#xff0c;角色包括主持人、技术专家和人文学者。过去&#xff0c;这意味着反复录音、剪辑、配音调整&…

作者头像 李华
网站建设 2026/4/12 0:38:34

[内网流媒体] 日志缺失对内网视音频系统意味着什么

问题陈述 不少内网实时画面工具上线时为了“简单”省掉了日志,结果故障时无人能查、责任不清、性能问题无从下手。日志缺失不仅是排障难题,还涉及合规与安全风险。 没有日志的后果 无法复盘故障:崩溃、卡顿、丢帧原因不明,修复靠猜。 无法审计访问:谁看过什么、何时访问…

作者头像 李华
网站建设 2026/4/13 20:41:42

实战:NPM UNKNOWN USER CONFIG警告的排查与修复

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 模拟一个实际项目场景&#xff0c;生成一个包含UNKNOWN USER CONFIG警告的NPM配置文件&#xff08;.npmrc&#xff09;。使用AI工具逐步分析警告原因&#xff0c;提供具体的修复步…

作者头像 李华
网站建设 2026/4/13 0:48:21

JavaScript 核心特性综合实战 —— 从函数到对象的深度应用

函数 语法格式 // 创建函数/函数声明/函数定义 function 函数名(形参列表) {函数体return 返回值; }// 函数调用 函数名(实参列表) // 不考虑返回值 返回值 函数名(实参列表) // 考虑返回值函数定义并不会执行函数体内容&#xff0c;必须要调用才会执行&#xff0c;调…

作者头像 李华
网站建设 2026/4/3 5:47:52

ChromeDriver一键配置工具开发指南

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发跨平台的ChromeDriver管理工具&#xff0c;功能包括&#xff1a;1) 本地浏览器版本扫描 2) 云端版本库匹配 3) 多线程下载加速 4) 自动环境配置 5) 历史版本管理。要求提供GUI…

作者头像 李华