苏萱 發表於 2025-12-3 08:30:26

一文详解C++中的智能指针避坑指南

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>一个经典的崩溃代码</li><li>错误一:循环引用&mdash;&mdash;智能指针的「鬼打墙」</li><ul class="second_class_ul"><li>1.1 循环引用的典型场景</li><li>1.2 解决方案:weak_ptr的正确使用</li><li>1.3 深度分析:weak_ptr的工作原理</li><li>1.4 循环引用检测工具</li></ul><li>错误二:性能陷阱&mdash;&mdash;你以为的「零成本」抽象</li><ul class="second_class_ul"><li>2.1 shared_ptr的隐藏成本</li><li>2.2 make_shared vs shared_ptr构造函数</li><li>2.3 性能优化策略</li><li>2.4 原子操作的性能影响</li></ul><li>错误三:线程安全&mdash;&mdash;最危险的幻觉</li><ul class="second_class_ul"><li>3.1 shared_ptr的线程安全层级</li><li>3.2 典型线程安全问题</li><li>3.3 线程安全智能指针实现</li><li>3.4 多线程环境最佳实践</li></ul><li>综合案例:一个线程安全、高性能的对象池</li><ul class="second_class_ul"></ul><li>智能指针的三大「生存法则」</li><ul class="second_class_ul"><li>法则一:所有权设计优先</li><li>法则二:性能意识常驻</li><li>法则三:线程安全不假设</li><li>最终检查清单</li></ul><li>从「会用」到「精通」</li><ul class="second_class_ul"></ul></ul></div><p>你以为将<code>new</code>替换为<code>make_shared</code>就万事大吉?真相是,智能指针的陷阱比手动管理更隐蔽、更危险。本文将深入剖析循环引用、性能陷阱、线程安全这三大「暗礁」,让你从「自以为会」到「真正精通」。</p>
<p class="maodian"></p><h2>一个经典的崩溃代码</h2>
<p>如下代码展露了智能指针中的<strong>循环引用</strong>问题。</p>
<div class="jb51code"><pre class="brush:cpp;">// 这就是那个导致崩溃的简化版代码
class UserProfile {
    std::shared_ptr&lt;UserProfile&gt; recommend_to;// 推荐给谁
    // ... 其他数据
};

void create_recommendation_cycle() {
    auto user1 = std::make_shared&lt;UserProfile&gt;();
    auto user2 = std::make_shared&lt;UserProfile&gt;();
   
    user1-&gt;recommend_to = user2;// user1推荐user2
    user2-&gt;recommend_to = user1;// user2又推荐user1
   
    // 离开作用域后,引用计数永远不会归零!
    // 内存泄漏,最终OOM(内存耗尽)
}
</pre></div>
<p>这就是智能指针最讽刺的地方:<strong>你为了避免内存泄漏而使用它,结果它却导致了更隐蔽的内存泄漏</strong>。</p>
<p class="maodian"></p><h2>错误一:循环引用&mdash;&mdash;智能指针的「鬼打墙」</h2>
<p class="maodian"></p><h3>1.1 循环引用的典型场景</h3>
<div class="jb51code"><pre class="brush:cpp;">// 场景1:双向关联(父子节点)
class TreeNode {
public:
    std::shared_ptr&lt;TreeNode&gt; parent;
    std::vector&lt;std::shared_ptr&lt;TreeNode&gt;&gt; children;
   
    void add_child(std::shared_ptr&lt;TreeNode&gt; child) {
      children.push_back(child);
      child-&gt;parent = shared_from_this();// 致命错误!
    }
};

// 场景2:观察者模式中的相互持有
class Observer;
class Subject {
    std::vector&lt;std::shared_ptr&lt;Observer&gt;&gt; observers;
};

class Observer {
    std::shared_ptr&lt;Subject&gt; subject;// 互相持有!
};

// 场景3:缓存系统中的自引用
class CacheEntry {
    std::shared_ptr&lt;CacheEntry&gt; next_in_lru;// LRU链表
    std::shared_ptr&lt;CacheEntry&gt; prev_in_lru;
};
</pre></div>
<p class="maodian"></p><h3>1.2 解决方案:weak_ptr的正确使用</h3>
<div class="jb51code"><pre class="brush:cpp;">// 正确方案1:使用weak_ptr打破循环
class TreeNode {
private:
    std::weak_ptr&lt;TreeNode&gt; parent_;// 关键改变!
    std::vector&lt;std::shared_ptr&lt;TreeNode&gt;&gt; children_;
   
public:
    void set_parent(std::shared_ptr&lt;TreeNode&gt; parent) {
      parent_ = parent;// weak_ptr不会增加引用计数
    }
   
    std::shared_ptr&lt;TreeNode&gt; get_parent() const {
      return parent_.lock();// 尝试提升为shared_ptr
    }
   
    void add_child(std::shared_ptr&lt;TreeNode&gt; child) {
      children_.push_back(child);
      child-&gt;set_parent(shared_from_this());
    }
};

// 正确方案2:明确所有权关系
class Document {
    // 文档拥有页面(独占所有权)
    std::vector&lt;std::unique_ptr&lt;Page&gt;&gt; pages_;
   
    // 页面可以引用文档,但不拥有
    class Page {
      Document* document_;// 原始指针!安全吗?
      // 这里的关键:生命周期由Document管理
    };
};
</pre></div>
<p class="maodian"></p><h3>1.3 深度分析:weak_ptr的工作原理</h3>
<div class="jb51code"><pre class="brush:cpp;">// weak_ptr内部机制模拟
template&lt;typename T&gt;
class WeakPtr {
private:
    T* ptr_;                  // 指向实际对象
    ControlBlock* control_block_;// 与shared_ptr共享的控制块
   
public:
    // lock()方法的实现
    std::shared_ptr&lt;T&gt; lock() const noexcept {
      if(control_block_ &amp;&amp; control_block_-&gt;ref_count &gt; 0) {
            // 对象还活着,创建新的shared_ptr
            return std::shared_ptr&lt;T&gt;(*this);
      }
      return std::shared_ptr&lt;T&gt;();// 返回空shared_ptr
    }
   
    // 控制块结构
    struct ControlBlock {
      std::atomic&lt;size_t&gt; ref_count{1};   // 强引用计数
      std::atomic&lt;size_t&gt; weak_count{1};    // 弱引用计数
      T* object_ptr{nullptr};
      
      ~ControlBlock() {
            if(ref_count == 0) {
                delete object_ptr;// 只有强引用为0时才删除对象
            }
            // weak_count为0时删除控制块本身
      }
    };
};
</pre></div>
<p><strong>关键点</strong>:</p>
<ul><li><code>weak_ptr</code>不增加<strong>强引用计数</strong>,只增加<strong>弱引用计数</strong></li><li>对象销毁的条件:强引用计数 == 0</li><li>控制块销毁的条件:强引用计数 == 0 <strong>且</strong> 弱引用计数 == 0</li></ul>
<p class="maodian"></p><h3>1.4 循环引用检测工具</h3>
<div class="jb51code"><pre class="brush:cpp;">// 运行时检测工具
class CyclicReferenceDetector {
public:
    template&lt;typename T&gt;
    static bool has_cycle(const std::shared_ptr&lt;T&gt;&amp; start) {
      std::unordered_set&lt;void*&gt; visited;
      return detect_cycle(start, visited);
    }
   
private:
    template&lt;typename T&gt;
    static bool detect_cycle(const std::shared_ptr&lt;T&gt;&amp; current,
                            std::unordered_set&lt;void*&gt;&amp; visited) {
      if(!current) return false;
      
      void* address = current.get();
      if(visited.count(address)) {
            std::cerr &lt;&lt; "Cycle detected at: " &lt;&lt; typeid(T).name()
                     &lt;&lt; " [" &lt;&lt; address &lt;&lt; "]" &lt;&lt; std::endl;
            return true;
      }
      
      visited.insert(address);
      
      // 使用反射或手动注册来遍历成员
      // 这里简化,实际需要更复杂的机制
      return false;
    }
};

// 使用示例
void check_for_cycles() {
    auto obj = std::make_shared&lt;TreeNode&gt;();
    // ... 构建可能循环的结构
   
    if(CyclicReferenceDetector::has_cycle(obj)) {
      std::cerr &lt;&lt; "WARNING: Memory leak due to cyclic reference!" &lt;&lt; std::endl;
    }
}
</pre></div>
<p class="maodian"></p><h2>错误二:性能陷阱&mdash;&mdash;你以为的「零成本」抽象</h2>
<p class="maodian"></p><h3>2.1 shared_ptr的隐藏成本</h3>
<div class="jb51code"><pre class="brush:cpp;">// 性能测试:shared_ptr vs 原始指针
void benchmark_shared_ptr() {
    constexpr size_t ITERATIONS = 1000000;
    std::vector&lt;std::shared_ptr&lt;Data&gt;&gt; shared_ptrs;
    std::vector&lt;Data*&gt; raw_ptrs;
   
    // 创建开销
    auto start = std::chrono::high_resolution_clock::now();
    for(size_t i = 0; i &lt; ITERATIONS; ++i) {
      shared_ptrs.push_back(std::make_shared&lt;Data&gt;(i));
    }
    auto shared_create = std::chrono::high_resolution_clock::now() - start;
   
    start = std::chrono::high_resolution_clock::now();
    for(size_t i = 0; i &lt; ITERATIONS; ++i) {
      raw_ptrs.push_back(new Data(i));
    }
    auto raw_create = std::chrono::high_resolution_clock::now() - start;
   
    std::cout &lt;&lt; "创建开销:\n"
            &lt;&lt; "shared_ptr: "
            &lt;&lt; std::chrono::duration&lt;double, std::milli&gt;(shared_create).count()
            &lt;&lt; "ms\n"
            &lt;&lt; "原始指针: "
            &lt;&lt; std::chrono::duration&lt;double, std::milli&gt;(raw_create).count()
            &lt;&lt; "ms\n";
   
    // 拷贝开销
    start = std::chrono::high_resolution_clock::now();
    for(size_t i = 0; i &lt; ITERATIONS; ++i) {
      auto copy = shared_ptrs;// 原子操作!
    }
    auto shared_copy = std::chrono::high_resolution_clock::now() - start;
   
    std::cout &lt;&lt; "拷贝开销:\n"
            &lt;&lt; "shared_ptr: "
            &lt;&lt; std::chrono::duration&lt;double, std::milli&gt;(shared_copy).count()
            &lt;&lt; "ms (每个拷贝约"
            &lt;&lt; std::chrono::duration&lt;double, std::nano&gt;(shared_copy).count()/ITERATIONS
            &lt;&lt; "ns)\n";
}
</pre></div>
<p><strong>性能开销来源</strong>:</p>
<ul><li><strong>控制块分配</strong>:额外的一次内存分配(除非用<code>make_shared</code>)</li><li><strong>原子操作</strong>:引用计数的增减需要原子操作,影响多核性能</li><li><strong>缓存不友好</strong>:对象和控制块可能不在同一缓存行</li><li><strong>虚函数开销</strong>:自定义删除器和分配器可能引入间接调用</li></ul>
<p class="maodian"></p><h3>2.2 make_shared vs shared_ptr构造函数</h3>
<div class="jb51code"><pre class="brush:cpp;">// 关键区别:内存布局
class LargeObject {
    char data;// 1KB数据
};

void memory_layout_demo() {
    // 方式1:两次内存分配
    std::shared_ptr&lt;LargeObject&gt; p1(new LargeObject);
    // 堆布局:[控制块] ...
    // 两次分配,可能内存碎片
   
    // 方式2:一次内存分配(推荐)
    auto p2 = std::make_shared&lt;LargeObject&gt;();
    // 堆布局:[控制块 + LargeObject]
    // 单次分配,更好的局部性
   
    // 但注意:weak_ptr会阻止整个内存块释放
    std::weak_ptr&lt;LargeObject&gt; weak = p2;
    p2.reset();// LargeObject析构,但内存直到weak销毁才释放
}
</pre></div>
<p class="maodian"></p><h3>2.3 性能优化策略</h3>
<div class="jb51code"><pre class="brush:cpp;">// 策略1:优先使用unique_ptr
class ConnectionPool {
private:
    // 池拥有所有连接
    std::vector&lt;std::unique_ptr&lt;Connection&gt;&gt; connections_;
   
    // 借出时返回原始指针或引用
    Connection* borrow_connection() {
      return connections_.get();
    }
   
    // unique_ptr没有引用计数开销
    // 所有权清晰,零额外成本
};

// 策略2:传递const引用而不是拷贝shared_ptr
void process_data_bad(const std::shared_ptr&lt;Data&gt;&amp; data) {
    // 这里看似没有拷贝,但可能在其他地方有
    auto local_copy = data;// 原子递增!
    // ...
}

void process_data_good(const Data&amp; data) {// 直接传递引用
    // 没有引用计数操作
    // 调用者需保证data的生命周期
}

// 策略3:局部使用shared_ptr,长期使用weak_ptr
class SessionManager {
private:
    std::unordered_map&lt;SessionId, std::weak_ptr&lt;Session&gt;&gt; sessions_;
   
public:
    std::shared_ptr&lt;Session&gt; get_session(SessionId id) {
      if(auto it = sessions_.find(id); it != sessions_.end()) {
            if(auto session = it-&gt;second.lock()) {
                return session;// 会话还活着
            }
            sessions_.erase(it);// 会话已过期,清理
      }
      return nullptr;
    }
   
    void register_session(SessionId id, std::shared_ptr&lt;Session&gt; session) {
      sessions_ = session;// 存储weak_ptr,不阻止销毁
    }
};
</pre></div>
<p class="maodian"></p><h3>2.4 原子操作的性能影响</h3>
<div class="jb51code"><pre class="brush:cpp;">// shared_ptr引用计数的原子操作(简化版)
template&lt;typename T&gt;
class SharedPtr {
    T* ptr;
    std::atomic&lt;long&gt;* ref_count;// 原子类型
   
public:
    SharedPtr(const SharedPtr&amp; other) : ptr(other.ptr), ref_count(other.ref_count) {
      // 内存屏障!影响多核性能
      ref_count-&gt;fetch_add(1, std::memory_order_relaxed);
    }
   
    ~SharedPtr() {
      if(ref_count-&gt;fetch_sub(1, std::memory_order_acq_rel) == 1) {
            delete ptr;
            delete ref_count;
      }
    }
};

// 性能对比:单线程 vs 多线程
void atomic_overhead_demo() {
    std::atomic&lt;int&gt; atomic_counter{0};
    int non_atomic_counter = 0;
   
    constexpr int ITERATIONS = 10000000;
   
    // 单线程性能
    auto start = std::chrono::high_resolution_clock::now();
    for(int i = 0; i &lt; ITERATIONS; ++i) {
      atomic_counter.fetch_add(1, std::memory_order_relaxed);
    }
    auto atomic_time = std::chrono::high_resolution_clock::now() - start;
   
    start = std::chrono::high_resolution_clock::now();
    for(int i = 0; i &lt; ITERATIONS; ++i) {
      ++non_atomic_counter;
    }
    auto non_atomic_time = std::chrono::high_resolution_clock::now() - start;
   
    std::cout &lt;&lt; "原子操作开销: "
            &lt;&lt; std::chrono::duration&lt;double, std::milli&gt;(atomic_time).count() /
               std::chrono::duration&lt;double, std::milli&gt;(non_atomic_time).count()
            &lt;&lt; "倍\n";
}
</pre></div>
<p class="maodian"></p><h2>错误三:线程安全&mdash;&mdash;最危险的幻觉</h2>
<p class="maodian"></p><h3>3.1 shared_ptr的线程安全层级</h3>
<div class="jb51code"><pre class="brush:cpp;">// shared_ptr的线程安全是分层的:
class ThreadSafetyLevels {
    // 级别1:控制块线程安全(标准保证)
    //   - 引用计数的增减是原子的
    //   - 不同的shared_ptr实例可以被不同线程安全地析构
   
    // 级别2:指向的数据线程不安全!
    //   - shared_ptr不保证其管理的对象的线程安全
    //   - 多个线程同时读写同一个对象需要外部同步
   
    // 级别3:同一个shared_ptr实例的读写不安全!
    //   - 同一个shared_ptr对象被多个线程读写需要同步
};

// 证明:shared_ptr内部不保护对象
void concurrent_access_problem() {
    auto shared_data = std::make_shared&lt;std::vector&lt;int&gt;&gt;();
   
    // 线程1:修改数据
    std::thread t1([&amp;shared_data]() {
      for(int i = 0; i &lt; 1000; ++i) {
            shared_data-&gt;push_back(i);// 竞态条件!
      }
    });
   
    // 线程2:同时读取
    std::thread t2([&amp;shared_data]() {
      for(int i = 0; i &lt; 1000; ++i) {
            if(!shared_data-&gt;empty()) {
                int value = shared_data-&gt;back();// 可能读取到无效数据!
            }
      }
    });
   
    t1.join();
    t2.join();
    // 结果:未定义行为!可能崩溃或数据损坏
}
</pre></div>
<p class="maodian"></p><h3>3.2 典型线程安全问题</h3>
<div class="jb51code"><pre class="brush:cpp;">// 问题1:错误的「线程安全」假设
class ThreadUnsafeCache {
    std::unordered_map&lt;std::string, std::shared_ptr&lt;Data&gt;&gt; cache_;
    std::mutex mutex_;
   
public:
    std::shared_ptr&lt;Data&gt; get(const std::string&amp; key) {
      std::lock_guard&lt;std::mutex&gt; lock(mutex_);
      if(auto it = cache_.find(key); it != cache_.end()) {
            return it-&gt;second;// 看似安全...
      }
      return nullptr;
    }
   
    // 问题:返回的shared_ptr可能被多个线程同时持有
    // 它们可以同时修改Data,而Data没有内置的线程保护!
};

// 问题2:shared_ptr的原子操作误解
void atomic_shared_ptr_misconception() {
    std::shared_ptr&lt;int&gt; p = std::make_shared&lt;int&gt;(42);
   
    std::thread t1([&amp;p]() {
      auto local_copy = p;// 引用计数原子递增
      // 但p.reset()可能同时发生!
    });
   
    std::thread t2([&amp;p]() {
      p.reset(new int(100));// 修改p本身需要同步!
    });
   
    t1.join();
    t2.join();
    // 这里有两个独立的数据竞争:
    // 1. 对p本身的修改(shared_ptr对象)
    // 2. 对新旧int对象的访问
};
</pre></div>
<p class="maodian"></p><h3>3.3 线程安全智能指针实现</h3>
<div class="jb51code"><pre class="brush:cpp;">// 方案1:使用atomic_shared_ptr(C++20)
#include &lt;atomic&gt;
#include &lt;memory&gt;

void cpp20_atomic_shared_ptr() {
    std::atomic&lt;std::shared_ptr&lt;int&gt;&gt; atomic_ptr;
   
    // 线程安全地存储
    std::thread writer([&amp;atomic_ptr]() {
      atomic_ptr.store(std::make_shared&lt;int&gt;(42));
    });
   
    // 线程安全地加载
    std::thread reader([&amp;atomic_ptr]() {
      std::shared_ptr&lt;int&gt; local = atomic_ptr.load();
      if(local) {
            // 安全读取local指向的内容
            // 但多个reader可能同时读取,内容本身需要保护
      }
    });
   
    writer.join();
    reader.join();
}

// 方案2:手动实现带锁的智能指针
template&lt;typename T&gt;
class ThreadSafeSharedPtr {
private:
    struct ControlBlock {
      T* ptr;
      std::atomic&lt;size_t&gt; ref_count;
      std::mutex data_mutex;// 保护对象本身
      
      // 自定义删除器,确保安全销毁
      void safe_delete() {
            std::lock_guard&lt;std::mutex&gt; lock(data_mutex);
            delete ptr;
            ptr = nullptr;
      }
    };
   
    ControlBlock* cb_;
   
public:
    // 提供线程安全的访问接口
    template&lt;typename Func&gt;
    auto with_lock(Func&amp;&amp; func) {
      std::lock_guard&lt;std::mutex&gt; lock(cb_-&gt;data_mutex);
      return std::forward&lt;Func&gt;(func)(*cb_-&gt;ptr);
    }
   
    // 线程安全的reset
    void reset(T* new_ptr = nullptr) {
      if(cb_ &amp;&amp; cb_-&gt;ref_count.fetch_sub(1) == 1) {
            cb_-&gt;safe_delete();
            delete cb_;
      }
      if(new_ptr) {
            cb_ = new ControlBlock{new_ptr, 1};
      } else {
            cb_ = nullptr;
      }
    }
};
</pre></div>
<p class="maodian"></p><h3>3.4 多线程环境最佳实践</h3>
<div class="jb51code"><pre class="brush:cpp;">// 最佳实践1:使用不可变数据
class ImmutableData {
private:
    const std::vector&lt;int&gt; data_;// 构造后不可变
   
public:
    // 线程安全:多个线程可以同时读取
    int get(size_t index) const {
      return data_.at(index);
    }
   
    // 创建新版本而不是修改
    std::shared_ptr&lt;ImmutableData&gt; with_addition(int value) const {
      auto new_data = std::make_shared&lt;ImmutableData&gt;(*this);
      // 注意:这里需要实际的不可变实现
      return new_data;
    }
};

// 最佳实践2:明确的所有权传递
class ThreadSafeMessageQueue {
private:
    struct Message {
      std::unique_ptr&lt;Data&gt; data;// 独占所有权
      // unique_ptr明确表示:只有一个线程拥有
    };
   
    std::queue&lt;Message&gt; queue_;
    std::mutex queue_mutex_;
    std::condition_variable cv_;
   
public:
    // 生产者:转移所有权到队列
    void push(std::unique_ptr&lt;Data&gt; data) {
      {
            std::lock_guard&lt;std::mutex&gt; lock(queue_mutex_);
            queue_.push(Message{std::move(data)});
      }
      cv_.notify_one();
    }
   
    // 消费者:从队列获取所有权
    std::unique_ptr&lt;Data&gt; pop() {
      std::unique_lock&lt;std::mutex&gt; lock(queue_mutex_);
      cv_.wait(lock, { return !queue_.empty(); });
      
      Message msg = std::move(queue_.front());
      queue_.pop();
      
      return std::move(msg.data);// 所有权转移给消费者
    }
};

// 最佳实践3:使用shared_ptr的别名构造函数
class ThreadSafeObserver {
private:
    std::shared_ptr&lt;std::mutex&gt; mutex_;// 共享的mutex
    std::shared_ptr&lt;Data&gt; data_;          // 共享的数据
   
public:
    ThreadSafeObserver(std::shared_ptr&lt;Data&gt; data)
      : data_(data)
      , mutex_(std::make_shared&lt;std::mutex&gt;()) {}
   
    void process() {
      std::lock_guard&lt;std::mutex&gt; lock(*mutex_);
      // 安全地访问data_
      // data_和mutex_的生命周期绑定在一起
    }
   
    // 创建观察者副本
    ThreadSafeObserver clone() const {
      return ThreadSafeObserver(data_);// 共享相同的mutex和数据
    }
};
</pre></div>
<p class="maodian"></p><h2>综合案例:一个线程安全、高性能的对象池</h2>
<div class="jb51code"><pre class="brush:cpp;">// 完整的最佳实践示例
template&lt;typename T&gt;
class ThreadSafeObjectPool {
private:
    struct PooledObject {
      T object;
      bool in_use{false};
      std::chrono::steady_clock::time_point last_used;
    };
   
    // 使用unique_ptr管理池中对象
    std::vector&lt;std::unique_ptr&lt;PooledObject&gt;&gt; pool_;
   
    // 可用的对象使用weak_ptr引用
    std::vector&lt;std::weak_ptr&lt;T&gt;&gt; available_;
   
    // 线程安全
    mutable std::shared_mutex mutex_;
   
    // 避免循环引用的关键:自定义删除器
    struct PoolDeleter {
      ThreadSafeObjectPool* pool;
      
      void operator()(T* ptr) {
            // 不是真的删除,而是返回池中
            pool-&gt;return_to_pool(ptr);
      }
    };
   
public:
    // 获取对象:返回带自定义删除器的shared_ptr
    std::shared_ptr&lt;T&gt; acquire() {
      std::unique_lock lock(mutex_);
      
      // 清理过期的weak_ptr
      available_.erase(
            std::remove_if(available_.begin(), available_.end(),
                [](const std::weak_ptr&lt;T&gt;&amp; wp) { return wp.expired(); }),
            available_.end()
      );
      
      // 尝试从可用对象中获取
      for(auto it = available_.begin(); it != available_.end(); ++it) {
            if(auto sp = it-&gt;lock()) {
                // 找到可用对象
                available_.erase(it);
               
                // 查找对应的PooledObject并标记为使用中
                for(auto&amp; pooled_obj : pool_) {
                  if(&amp;pooled_obj-&gt;object == sp.get()) {
                        pooled_obj-&gt;in_use = true;
                        pooled_obj-&gt;last_used = std::chrono::steady_clock::now();
                        break;
                  }
                }
               
                return sp;
            }
      }
      
      // 创建新对象
      auto pooled_obj = std::make_unique&lt;PooledObject&gt;();
      T* raw_ptr = &amp;pooled_obj-&gt;object;
      pooled_obj-&gt;in_use = true;
      pooled_obj-&gt;last_used = std::chrono::steady_clock::now();
      
      // 创建带自定义删除器的shared_ptr
      std::shared_ptr&lt;T&gt; sp(raw_ptr, PoolDeleter{this});
      
      // 存储unique_ptr以管理生命周期
      pool_.push_back(std::move(pooled_obj));
      
      return sp;
    }
   
private:
    // 对象返回到池中(由自定义删除器调用)
    void return_to_pool(T* ptr) {
      std::unique_lock lock(mutex_);
      
      // 查找对应的PooledObject
      for(auto&amp; pooled_obj : pool_) {
            if(&amp;pooled_obj-&gt;object == ptr) {
                pooled_obj-&gt;in_use = false;
               
                // 创建新的weak_ptr添加到可用列表
                available_.push_back(
                  std::shared_ptr&lt;T&gt;(std::shared_ptr&lt;T&gt;{}, ptr)// 别名构造函数
                );
                break;
            }
      }
    }
   
    // 清理长时间未用的对象
    void cleanup_old_objects(std::chrono::seconds max_idle_time) {
      std::unique_lock lock(mutex_);
      
      auto now = std::chrono::steady_clock::now();
      
      for(auto it = pool_.begin(); it != pool_.end(); ) {
            auto&amp; pooled_obj = *it;
            
            if(!pooled_obj-&gt;in_use &amp;&amp;
               (now - pooled_obj-&gt;last_used) &gt; max_idle_time) {
                // 从available_中移除对应的weak_ptr
                available_.erase(
                  std::remove_if(available_.begin(), available_.end(),
                        (const std::weak_ptr&lt;T&gt;&amp; wp) {
                            if(auto sp = wp.lock()) {
                              return sp.get() == obj_ptr;
                            }
                            return false;
                        }),
                  available_.end()
                );
               
                // 删除对象
                it = pool_.erase(it);
            } else {
                ++it;
            }
      }
    }
};

// 使用示例
void use_object_pool() {
    ThreadSafeObjectPool&lt;DatabaseConnection&gt; pool;
   
    // 多线程安全地获取连接
    std::vector&lt;std::thread&gt; threads;
    for(int i = 0; i &lt; 10; ++i) {
      threads.emplace_back([&amp;pool, i]() {
            // 获取连接(可能阻塞直到有可用连接)
            auto conn = pool.acquire();
            
            // 使用连接
            conn-&gt;execute_query("SELECT * FROM users");
            
            // conn离开作用域,自动返回到池中
            // 因为使用了自定义删除器
      });
    }
   
    for(auto&amp; t : threads) {
      t.join();
    }
}
</pre></div>
<p class="maodian"></p><h2>智能指针的三大「生存法则」</h2>
<p class="maodian"></p><h3>法则一:所有权设计优先</h3>
<ul><li>能使用<code>unique_ptr</code>就不要用<code>shared_ptr</code></li><li>明确对象的所有权生命周期</li><li>使用<code>weak_ptr</code>打破循环引用</li></ul>
<p class="maodian"></p><h3>法则二:性能意识常驻</h3>
<ul><li>优先使用<code>make_shared</code>/<code>make_unique</code></li><li>避免不必要的<code>shared_ptr</code>拷贝</li><li>注意原子操作的开销</li></ul>
<p class="maodian"></p><h3>法则三:线程安全不假设</h3>
<ul><li><code>shared_ptr</code>的线程安全仅限于控制块</li><li>指向的数据需要额外保护</li><li>考虑使用不可变数据结构</li></ul>
<p class="maodian"></p><h3>最终检查清单</h3>
<p>每次使用智能指针前问自己:</p>
<p>1. 这个对象应该被谁拥有?</p>
<ul><li>unique_ptr</li><li>shared_ptr</li></ul>
<p>2. 是否有循环引用的可能?</p>
<ul><li>有(需weak_ptr)&nbsp;</li><li>无</li></ul>
<p>3. 是否会在多线程中使用?</p>
<ul><li>是(需同步)&nbsp;</li><li>否</li></ul>
<p>4. 是否需要最优性能?</p>
<ul><li>是(避免shared_ptr)</li><li>否</li></ul>
<p>5. 是否传递所有权?</p>
<ul><li>是(移动语义)</li><li>否(传递引用)</li></ul>
<p class="maodian"></p><h2>从「会用」到「精通」</h2>
<p>智能指针不是「银弹」,而是「双刃剑」。它解决了手动管理内存的烦恼,却引入了更隐蔽的陷阱。真正的精通,不是记住语法,而是理解每个设计决策背后的权衡。</p>
<p>正如C++之父Bjarne Stroustrup所说:「C++的设计初衷是让好的设计更容易,坏的设计更困难」。智能指针正是这一哲学的体现&mdash;&mdash;它奖励清晰的所有权设计,惩罚模糊的资源管理。</p>
<p>下次当你写下<code>std::shared_ptr</code>时,不妨停顿一秒,问问自己:「我真的需要共享所有权吗?」这个简单的问题,可能就是避免下一个深夜崩溃的关键。</p>
<p>到此这篇关于一文详解C++中的智能指针避坑指南的文章就介绍到这了,更多相关C++智能指针内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>C++智能指针weak_ptr创建和使用详解</li><li>C++&nbsp;智能指针原理、使用与最佳实践指南</li><li>C++智能指针的补充和特殊类的设计示例详解</li><li>深入解析C++中的智能指针</li><li>C++进阶异常处理与智能指针实战指南</li><li>C++中智能指针weak_ptr的原理及使用</li><li>C++智能指针的使用</li><li>C++&nbsp;智能指针使用不当导致内存泄漏问题解析</li><li>C++&nbsp;QT智能指针的使用详解</li><li>C++共享智能指针shared_ptr的实现</li><li>C++四个智能指针的使用小结</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: 一文详解C++中的智能指针避坑指南