weak的实现原理

本文详细解析了弱引用的工作原理及其实现方式。通过构建一个包含被弱引用对象及其所有弱引用地址的数据结构,实现对象被销毁时自动将弱引用设为nil的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现 weak
weak 的作用

weak 关键字的作用弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为 nil

如何实现 weak
  1. 现在我们将 weak 的思路整理一下:

  2. 整个系统中存在很多个对象,这些对象都可能会被弱引用,那么我们需要一个容器来容纳这些被弱引用的对象,比如数组,在此将这个容器的数据结构标识为 objectContainerDataStructure

  3. 一个对象可能会被多次弱引用,当这个对象被销毁时,我们需要找到这个对象的所有弱引用,所以我们需要将这些弱引用的地址(即指针)放在一个容器里,比如数组,在此将这些弱引用的地址的数据结构标识为 pointerContainerDataStructure

  4. 当对象不再被强引用时需要销毁的时候,我们需要通过这个对象在 objectContainerDataStructure 找到其对应的 pointerContainerDataStructure,进而找到这个对象的所有弱引用,将其置为 nil

  5. 通过上面的步骤,我们大概可以得出这么一个数据结构:

  6. pointerContainerDataStructure 仅仅只是容纳一个对象的所有弱引用的地址,所以用数组即可;

  7. objectContainerDataStructure 是一个 key-value 数据结构,将对象作为 key,对象的内存地址是最好的选择;

  8. iOS 中常用的 key-value 数据结构就是字典 Dictionary ,在这里我们的 key 是一个数值对象,value 则是一个数值数组对象,可以用哈希表实现;

  9. 总结

为了实现 weak,我们需要这样的一张弱引用表:

  1. 表的数据结构是哈希表;

  2. 表的 key 是对象的内存地址;

  3. value 是指向该对象的所有弱引用的指针;


### Shared Pointer 的实现原理 在现代 C++ 中,`std::shared_ptr` 是一种广泛使用的智能指针类型,用于管理动态分配的对象。它允许多个 `shared_ptr` 实例共同拥有同一对象,并自动处理对象的生命周期管理。以下是关于其实现原理的关键点: #### 1. 参考计数机制 `shared_ptr` 使用了一个内部控制块(control block)来跟踪引用次数。每当一个新的 `shared_ptr` 复制已有的 `shared_ptr` 或者通过赋值操作共享所有权时,引用计数就会增加;而当某个 `shared_ptr` 被销毁或者重置时,引用计数则会减少。只有当引用计数降为零时,才会调用析构函数释放所管理的对象[^3]。 ```cpp struct ControlBlock { int ref_count; // 引用计数 int weak_ref_count; // Weak pointer 计数 void (*deleter)(void*); // 自定义删除器 }; ``` #### 2. 控制块的内存布局 为了支持多线程环境中的安全性和高效性,`shared_ptr` 和 `weak_ptr` 共享同一个控制块。这意味着即使所有的 `shared_ptr` 已经被销毁,只要还有存活的 `weak_ptr` 存在,控制块就不会被销毁。这使得可以通过 `weak_ptr.lock()` 方法重新获取新的 `shared_ptr` 实例[^4]。 #### 3. 构造与析构过程 - 当创建一个 `shared_ptr` 并传入裸指针时,会初始化一个新的控制块并将引用计数设置为 1。 - 在复制构造或赋值过程中,目标 `shared_ptr` 将指向相同的控制块,并将其引用计数加一。 - 销毁 `shared_ptr` 或调用 `reset()` 函数时,引用计数减一。如果引用计数变为零,则执行自定义删除器以释放托管对象,并清理控制块。 #### 4. 动态转换的支持 借助于模板特化以及 RTTI(Run-Time Type Information),`dynamic_pointer_cast` 提供了一种类型安全的方式来进行跨继承层次关系的指针转换。这种行为类似于传统的 `dynamic_cast`,但它作用于 `shared_ptr` 类型之上[^1]。 ```cpp template<class T, class U> shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r) noexcept; ``` #### 5. 安全性考量 为了避免悬空指针问题的发生,`shared_ptr` 设计成只能够通过对相同原始指针进行拷贝的方式来建立关联。这样可以确保所有参与共享的对象都知晓彼此的存在状态变化[^3]。 --- ### 示例代码展示 以下是一个简化版的手动实现示例,展示了核心概念: ```cpp #include <iostream> #include <memory> // 手工模拟 control block 结构 struct ManualControlBlock { int strong_refs = 0; // Strong reference count (shared_ptr) int weak_refs = 0; // Weak reference count (weak_ptr) void increment_strong() { ++strong_refs; } bool decrement_strong() { return --strong_refs == 0; } void increment_weak() { ++weak_refs; } bool decrement_weak() { return --weak_refs == 0; } }; class ManualSharedPtr { private: int* ptr_; // Managed raw pointer ManualControlBlock* cb_; // Reference counting block public: explicit ManualSharedPtr(int* p = nullptr) : ptr_(p), cb_(new ManualControlBlock()) { if (ptr_) cb_->increment_strong(); } ~ManualSharedPtr() { if (!cb_ || !cb_->decrement_strong()) return; delete ptr_; if (cb_->decrement_weak()) delete cb_; } ManualSharedPtr(const ManualSharedPtr& other) : ptr_(other.ptr_), cb_(other.cb_) { if (cb_) cb_->increment_strong(); } ManualSharedPtr& operator=(const ManualSharedPtr& other) { if (this != &other) { ManualSharedPtr tmp(other); swap(tmp); } return *this; } void reset() { ManualSharedPtr().swap(*this); } void swap(ManualSharedPtr& rhs) noexcept { using std::swap; swap(ptr_, rhs.ptr_); swap(cb_, rhs.cb_); } }; ``` --- ### 总结 综上所述,`shared_ptr` 的主要特点是基于引用计数的垃圾回收策略,配合精细设计的控制块实现了高效的资源共享和安全性保障。同时提供了诸如 `dynamic_pointer_cast` 这样的工具增强灵活性^[]^。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值