news 2026/1/22 17:44:30

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/1/6 4:42:01

1小时搞定:构建你自己的命令行长度检测工具

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个轻量级命令行长度检测工具原型。功能包括&#xff1a;实时监测输入命令长度、与系统限制值对比、超过阈值时给出警告和建议。支持Windows和Linux系统&#xff0c;提供简单…

作者头像 李华
网站建设 2026/1/6 4:41:45

对比传统方式:AI生成Docker配置快10倍

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请对比生成两个方案&#xff1a;1. 传统手动编写的Node.js应用Docker配置 2. AI自动生成的优化版本。要求展示&#xff1a;1. 构建时间对比 2. 镜像层优化建议 3. 多阶段构建实现 …

作者头像 李华
网站建设 2026/1/21 4:19:39

print driver host for 32bit applications与用户态交互机制详解

print driver host for 32bit applications&#xff1a;跨架构打印兼容的幕后功臣在一台运行 Windows 10 或 11 的现代电脑上&#xff0c;你可能从未注意过一个名为splwow64.exe的进程。它安静地潜伏在任务管理器中&#xff0c;偶尔被触发&#xff0c;又迅速归于沉寂。但正是这…

作者头像 李华
网站建设 2026/1/6 4:41:24

Qdrant入门指南:5分钟搭建第一个向量搜索应用

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个最简单的Qdrant入门示例&#xff0c;使用Docker快速部署Qdrant服务。功能要求&#xff1a;1. 用Python脚本插入10条文本嵌入向量&#xff1b;2. 实现一个查询接口找到最相…

作者头像 李华
网站建设 2026/1/6 4:41:22

3分钟完成Windows Docker安装:极速配置方案

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个极简但完整的Windows Docker一键安装脚本。要求&#xff1a;1. 仅保留必要安装步骤 2. 集成阿里云镜像加速 3. 跳过不必要的配置向导 4. 自动设置环境变量 5. 输出简洁的安…

作者头像 李华
网站建设 2026/1/6 4:41:19

MODBUS入门指南:从零开始学工业通信协议

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个交互式MODBUS学习演示程序&#xff0c;包含&#xff1a;1. 协议基础知识讲解 2. 报文构造模拟器 3. 功能码练习模块 4. 简单问答测试 5. 可视化通信过程演示。要求界面友好…

作者头像 李华