类C如何处理存储的类D对象引用的有效性问题
类C处理D对象引用有效性的最优方案
问题背景
类C作为库组件,持有存储D对象指针的vector,当调用方的D对象超出作用域销毁后,需要避免C访问悬垂指针。
现有方案分析
方案1:D析构时主动从C的列表中移除
- 弊端:这种设计会让D和C产生强耦合,D必须知晓C的内部存储结构(比如
_list),违反了单一职责原则和封装性。如果后续C的存储结构变更(比如从vector改成unordered_set),所有D类的相关代码都要修改,维护成本极高。另外,若存在多个C实例持有同一个D的指针,D的析构逻辑会变得复杂,容易出现遗漏或错误。
方案2:改用weak_ptr存储
- 可行性:这是生产环境中常用的成熟方案。将
vector<D*>改为vector<weak_ptr<D>>,每次访问前调用weak_ptr::lock()获取shared_ptr,如果lock()返回空,说明对象已销毁,直接跳过即可。 - 注意事项:需要调用方在创建D对象时使用
shared_ptr(比如auto d = make_shared<D>()),再将weak_ptr传递给C。weak_ptr不会增加引用计数,因此不会延长D对象的生命周期,同时能安全检测对象是否存活。
优化后的示例代码
#include <memory> #include <vector> #include <algorithm> #include <cstdio> struct D { ~D() { printf ("~D()\n"); } }; class C { std::vector<std::weak_ptr<D>> _list; public: void add(std::weak_ptr<D> dObject) { _list.push_back(dObject); printf ("Adding D => size = %ld\n", _list.size()); } // 遍历并处理所有有效对象,同时清理失效引用 void processValidObjects() { // 先清理已失效的weak_ptr,避免列表冗余膨胀 _list.erase(std::remove_if(_list.begin(), _list.end(), [](const std::weak_ptr<D>& wp) { return wp.expired(); }), _list.end()); for (auto& wp : _list) { if (auto sp = wp.lock()) { // 此时sp指向的D对象一定有效,可安全访问 printf("Processing valid D object\n"); } } } ~C() { printf ("~C()\n"); } }; int main() { C c1; { auto d1 = std::make_shared<D>(); c1.add(d1); } printf ("----out of scope---\n"); auto d2 = std::make_shared<D>(); c1.add(d2); c1.processValidObjects(); }
额外建议
- 如果调用方无法使用
shared_ptr(比如D对象是栈上分配的),可以采用观察者模式:C作为观察者,D作为被观察者,D在析构时通知所有持有它引用的C实例移除自身。这种方式比方案1更解耦,D只需要维护一个观察者列表,不需要知晓C的内部存储细节。 - 定期清理C中已失效的引用(比如在
add或processValidObjects方法中),避免容器积累大量无效指针,影响后续遍历性能。
内容的提问来源于stack exchange,提问作者xyf
相关产品推荐
相关产品推荐

