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

C++中指针,智能指针的理解

我的理解:

原始指针:


把内存当做开灯的房间。
指针是该房间的地址。
类型等其他细节决定该房间多大。
例如:
int* a = new(int);
*a = 4;
我们创建了一个大小为4字节的房间来存放4,又将其地址告诉了a。
a本身也是要求开了一个房间,房间里面存放大小为4字节并现在存放4的房间的地址。
这样我们就可以通过a给出的地址,直接访问这个房间,确定是否还在使用,不使用我就给他关灯释放了,或者给别人使用。
这个时候就有俩问题了,如果我们把a所在的房间释放掉了,例如a随其作用域被销毁,但是a中存放了另一个房间的地址,没人知道了

那这个房间灯就一直开着,因为没有人给他关灯了,这就导致了内存泄漏。
还有一个问题,假如:

```
int *a = new(int);
*a = 4;
int *b = a;
delete b;
*a = 6;
```

a把房间地址告诉了b,b自己把房间灯关了,地址销毁了,但没有告诉a。这个时候,a变成了悬空指针,指向了被释放的房间,我不确定这个房间是否又被别人使用了,这时a进房间操作,或者直接把房间灯关了。都会导致问题!

为了防止自己忘记关灯,或者防止操作悬空指针。

C++中引入了智能指针,来更方便的管理房间(内存)

关于智能指针:


unique_ptr:

相当于我创建一个房间开灯时,直接找了一个负责人,让他不用的时候把这个房间关灯释放。这个房间也只有他一个人可以进,这样可以避免房间灯忘记关了,或者已经被关了,有人不知道,还去访问操作。

shared_ptr:

相当于我创建一个房间开灯,但这个房间是公共自习室,可以很多人进入这个房间操作,不过进门需要登记,出门也需要登记,去门登记时发现自己是自习室最后一个人,那他就要负责将灯关了,门锁上。

weak_ptr:

属于是访客,shared-ptr申请打开的房间,本身只允许shared-ptr进,weak_ptr,本身没有进入权限,他想进房间,需要经过让里面的shared_ptr给他开门,所以他也不需要登记,因为他只是访客,不走正规流程。如果房间里有shared-ptr,他可以进去,如果没有shared-ptr,他就没人开门进不去。所以weak_ptr也可以用来确定这个房间是否被关灯释放,如果其进不去,就说明房间已经被关灯释放了。

Deepseek指正分析:

您对C++中指针和智能指针的理解非常准确,而且比喻得很生动形象!让我逐一点评并补充一些细节:

您的理解完全正确:

1.原始指针的比喻

  • 房间地址:指针确实是内存地址

  • 房间大小:类型决定了房间的大小(如int通常是4字节)

  • 内存泄漏:完全正确,忘记关灯(不delete)导致房间一直亮着

  • 悬空指针:b关灯后a不知道,还想去访问,这就是典型的悬空指针问题

2.智能指针的比喻

unique_ptr:

✅ "只有一个负责人" - 这正是unique_ptr的独占所有权语义
✅ "负责关灯" - unique_ptr离开作用域时自动释放内存

shared_ptr:

✅ "公共自习室,需要登记" - 完美的引用计数比喻
✅ "最后一个人负责关灯" - 引用计数降为0时自动释放

weak_ptr:

✅ "访客需要里面的人开门" - weak_ptr需要通过lock()获取shared_ptr
✅ "不负责关灯" - 不增加引用计数
✅ "可以用来检查房间状态" - 检查资源是否还存在

补充一些技术细节:

unique_ptr的"移动而非复制"

// 正确示例 - 移动所有权 std::unique_ptr<int> a = std::make_unique<int>(4); std::unique_ptr<int> b = std::move(a); // a交出所有权给b // 错误示例 // std::unique_ptr<int> c = a; // 编译错误!不能复制

shared_ptr的循环引用问题

class Person { public: std::shared_ptr<Person> partner; }; auto alice = std::make_shared<Person>(); auto bob = std::make_shared<Person>(); alice->partner = bob; // bob引用计数+1 = 2 bob->partner = alice; // alice引用计数+1 = 2 // 离开作用域时: // bob引用计数-1 = 1(因为alice还持有) // alice引用计数-1 = 1(因为bob还持有) // 结果:内存泄漏!两人互相持有,谁也关不了灯

weak_ptr解决循环引用

class Person { public: std::weak_ptr<Person> partner; // 使用weak_ptr避免循环引用 }; auto alice = std::make_shared<Person>(); auto bob = std::make_shared<Person>(); alice->partner = bob; // weak_ptr,不增加引用计数 bob->partner = alice; // weak_ptr,不增加引用计数 // 离开作用域时都能正确释放

现代C++最佳实践:

  1. 优先使用智能指针,避免原始指针管理内存

  2. 能用unique_ptr就用unique_ptr,更轻量、更安全

  3. shared_ptr用于共享所有权,注意循环引用

  4. weak_ptr用于观察shared_ptr,不参与所有权管理

  5. 使用make_unique/make_shared(C++14/11):

    // 推荐 auto ptr = std::make_unique<int>(42); auto ptr2 = std::make_shared<int>(42); // 不推荐(可能的内存泄漏风险) std::shared_ptr<int> ptr(new int(42));

您的理解不仅正确,而且比喻非常贴切!这种形象的思维方式对于理解复杂的内存管理概念非常有帮助。保持这种学习方式,您对C++内存管理的掌握会更加深入。

关于指针的使用:

我的理解:

使用指针,可以避免拷贝,直接内存访问,更精细的内存布局控制,也可以实现更复杂的数据结构;与硬件进行交互,支持原地修改等优点。但是不规范的使用,容易引发内存安全。

关于智能指针中unique_ptr和shared_ptr的选择,

想让指针与生命周期绑定,确保同一时间只有一个人进入这个房间,用unique_ptr,unique_ptr比shared_ptr性能高一点点。

如果一片地址,需要多个人同时访问,且不确定其先后顺序,用share_ptr。

Deepseek指正补充:

关于原始指针的使用分析 ✅基本正确

优点总结得很好:

  1. 避免拷贝- 传递指针而非对象本身

  2. 直接内存访问- 低级别操作

  3. 精细内存控制- 自定义分配/对齐

  4. 复杂数据结构- 链表、树等必需

  5. 硬件交互- 寄存器映射、DMA等

  6. 原地修改- 函数内外同步修改

但可以更精确地补充:

// 避免拷贝的典型例子 void processLargeData(const BigObject* data) { // 传指针而非传值 // 避免了BigObject的完整拷贝 } // 但现代C++中,以下方式通常更安全: void processLargeData(const BigObject& data) { // 传引用 // 同样避免拷贝,但更安全 } void processLargeData(std::unique_ptr<BigObject> data) { // 移动语义 // 转移所有权,避免拷贝 }

关于智能指针选择的指正和补充 ✅方向正确,但有细节

1.unique_ptr 的正确理解

您说"确保同一时间只有一个人进入这个房间" - 这是对的,但需要补充:

更准确的比喻:unique_ptr 就像房间钥匙,不是可以复制的普通钥匙,而是银行保险库的钥匙卡:

  • 一次只能有一个人持有

  • 转移所有权需要明确交接(std::move

  • 无法复制,离开作用域自动归还钥匙(释放内存)

性能差异的原因:

// unique_ptr:通常是原始指针大小 + 可能的自定义删除器 sizeof(std::unique_ptr<int>) == 8 (64位系统) // shared_ptr:包含两个原始指针(对象指针+控制块指针) sizeof(std::shared_ptr<int>) == 16 (64位系统) // shared_ptr还需要维护引用计数的原子操作,有额外开销

2.shared_ptr 的补充说明

您说"需要多个人同时访问,且不确定其先后顺序" - 这是对的,但需要明确:

什么时候应该用 shared_ptr:

  1. 明确需要共享所有权时

  2. 生命周期不明确,由使用者决定时

  3. 需要多个地方持有同一资源,且任一持有者都可能最后释放时

但要注意滥用 shared_ptr 的情况:

// 不好的例子:其实只需要unique_ptr class Document { private: std::shared_ptr<Image> thumbnail_; // ❌ 过度设计 // 因为只有Document使用这个thumbnail // 应该用:std::unique_ptr<Image> thumbnail_; }; // 好的例子:明确共享 class ChatRoom { private: std::vector<std::shared_ptr<User>> users_; // ✅ 多个地方可能需要访问 // 因为User对象可能在其他地方也被引用 // 比如消息历史、用户列表等 };

3.具体选择指南(决策树)

问自己这些问题:

Q1: 这个资源有且只有一个所有者吗? ├── 是 → 使用 unique_ptr └── 否 → 进入Q2 Q2: 需要共享所有权吗? ├── 是 → 使用 shared_ptr(但先考虑是否设计有问题) └── 否 → 使用原始指针或引用(作为视图/view) Q3: 使用shared_ptr时,有循环引用风险吗? ├── 是 → 使用weak_ptr打破循环 └── 否 → 直接使用shared_ptr

4.现代C++中的最佳实践

// 1. 优先使用值语义,而不是指针 // 不好的传统做法: std::unique_ptr<Person> person(new Person()); // 现代做法(如果可能): Person person{}; // 直接在栈上 // 2. 工厂函数返回智能指针 std::unique_ptr<Database> Database::create() { return std::make_unique<Database>(); } // 3. 按需选择所有权模型 class Renderer { private: // 独占资源:unique_ptr std::unique_ptr<Shader> mainShader_; // 共享资源:shared_ptr std::shared_ptr<Texture> defaultTexture_; // 视图/观察:原始指针或引用 Camera* currentCamera_; // 不拥有,只是引用 }; // 4. 避免所有权传递混淆 void processData(std::shared_ptr<Data> data) { // 明确:共享所有权 // 调用者知道你会保留一份引用 } void processData(const std::unique_ptr<Data>& data) { // 不推荐 // 接口不清晰,改用Data*或Data& } void processData(Data* data) { // 明确:不取得所有权 // 只是使用,不负责释放 }

5.性能考虑的具体数据

  • unique_ptr:零开销抽象(编译器优化后≈原始指针)

  • shared_ptr

    • 2倍指针大小

    • 引用计数原子操作(多线程同步开销)

    • 控制块分配(如果用make_shared可以优化)

总结您的理解:

完全正确的方向性理解:

  • unique_ptr:独占所有权,性能更好

  • shared_ptr:共享所有权,不确定生命周期

🔧需要微调的技术细节

  1. 优先考虑是否真的需要指针(能用栈变量就用栈变量)

  2. 能用unique_ptr就不用shared_ptr(更简单安全)

  3. shared_ptr不是默认选择,而是明确需要共享时的选择

  4. 接口设计时要明确所有权语义

您已经掌握了核心概念,现在需要的是在实际编码中培养"所有权直觉"——这是区分C++新手和专家的关键!

http://icebutterfly214.com/news/91224/

相关文章:

  • 基于SSM+Vue的实习管理系统的设计与实现
  • OpenWrt负载均衡配置实战:告别网络拥堵的多线路叠加方案
  • 2025年Q4北京朝阳区装修公司排名:亿丰方圆16区服务更安心 - 品牌智鉴榜
  • 2025年热门的实验室 PVC 开炼机厂家最新TOP排行榜 - 行业平台推荐
  • Cowabunga终极指南:10分钟打造个性化iOS设备
  • 3000亿参数仅需2卡部署:ERNIE 4.5如何用2比特量化技术重塑企业AI格局
  • 【节点】[Adjustment-Hue节点]原理解析与实际应用
  • MobaXterm高效运维实战全攻略
  • 告别JSON/YAML翻译烦恼:免费开源神器json-translator全解析
  • 2025哈尔滨高品质卫浴产品TOP5推荐:甄选好货避坑指南, - myqiye
  • Windows Defender彻底移除终极指南:从技术原理到实战应用
  • 27、抵御垃圾邮件与保障Sendmail服务器安全
  • 滑动验证服务商如何选?2025年年终最新技术评测与权威推荐指南! - 十大品牌推荐
  • Three.js虚拟现实开发完整指南:性能优化与开发效率提升
  • 2025年热门的对接式垃圾车最新TOP厂家排名 - 行业平台推荐
  • 2025包装密封/CCIT/过滤器/医药包装完整性测试仪推荐厂家_质量好|性能好|性价比高|国内品牌排行榜 - 品牌推荐大师1
  • 终极情侣互动小程序开发指南:三步构建云端情感管理系统
  • Archivematica终极指南:快速掌握开源数字保存系统
  • 2025年质量好的超薄三节隐藏轨/阻尼三节隐藏轨优质厂家推荐榜单 - 行业平台推荐
  • 5步打造专业纸质知识库:从在线文档到实体印刷的完整指南
  • 如何在macOS上免费搭建企业级远程存储:iSCSI Initiator终极指南
  • 2025年知名的六元合金丝电刷热门厂家推荐榜单 - 行业平台推荐
  • 5分钟掌握DBeaver数据安全:智能备份与灾难恢复完全指南
  • 哪款护发素口碑好?干枯发难柔顺?9 款高口碑护发素谁是真救星? - 资讯焦点
  • Llama-Factory部署指南:本地与云端环境配置全攻略
  • 儿童教育内容过滤系统:Llama-Factory识别不良信息
  • 使用Llama-Factory进行Baichuan模型微调的完整流程与成本分析
  • 告别繁琐下载!Adobe Downloader一键获取全家桶的终极指南
  • Wan2.2-T2V-A14B模型对光线变化的模拟精度实测
  • 2025年TD5B低速自动平衡离心机/TD5多管架自动平衡离心机/TD4低速台式离心机/TD4G血型卡离心机生产厂家推荐 - 品牌推荐大师1