25年第二周:读书笔记
循环嵌套问题理解
两个类相互包含对方作为成员的情况,比如类A有一个类B的对象成员,而类B又有一个类A的对象成
.环形嵌套的核心问题是 编译时的循环依赖
和 运行时的资源管理
:
-
编译问题:两个类直接相互包含对方的头文件,导致编译器无法确定类的内存布局。
-
运行时问题:若两个类通过指针或智能指针互相持有对方,可能引发内存泄漏(如 shared_ptr 循环引用)或 double free(如手动释放原始指针)。
编译问题解决:
-
使用前置声明: 使用场景比较简单。
-
使用DIP 依赖倒置原则:适合模块之间,便于扩充,适合复杂长期使用, 不过有虚表,虚函数开销
运行时问题:
意味着使用指针, 原始指针,智能指针. 原始指针可能会导致double free, 空指针。
智能指针:要用shared_ptr 和weak_ptr混合使用。 不然混造成内存泄漏,引用计数无法归零
#include <memory>
#include <iostream>
using namespace std;
class B;
class A {
public:
shared_ptr<B> b_ptr;
~A() { cout << "A 被销毁" << endl; }
};
class B {
public:
weak_ptr<A> a_ptr; // 改为 weak_ptr
~B() { cout << "B 被销毁" << endl; }
};
int main() {
shared_ptr<A> a = make_shared<A>();
shared_ptr<B> b = make_shared<B>();
a->b_ptr = b;
b->a_ptr = a; // weak_ptr 不增加引用计数
// 此时引用计数:
// A 的引用计数 = 1(只有 a 持有)
// B 的引用计数 = 2(b 和 A::b_ptr)
return 0;
}
if (auto a = a_ptr.lock()) { // 提升为 shared_ptr
// 安全使用 a
} else {
// A 已被销毁
}
lock() 是原子操作,保证在多线程环境中,即使其他线程正在释放对象,也能安全地获取到最新的状态
-
如果先调用 expired() 再调用 lock(),中间可能发生对象被释放,导致竞态条件(Race Condition)。
-
直接使用 lock() 可以避免这一问题