循环引用是指多个对象通过std::shared_ptr
相互引用,形成一个闭环,导致它们的引用计数无法降为0.即使这些对象不再被程序的其他部分引用,它们的内存也不会被释放,因为每个对象的std::shared_ptr
都被其他对象持有。循环引用会导致内存泄漏,因为资源无法被析构。std::shared_ptr
的引用计数机制依赖于计数归零来释放资源,循环引用破坏了这一机制。
以下示例展示了两个类A
和B
相互持有对方的std::shared_ptr
:
cpp
#include <iostream>
#include <memory>
class B; // 前向声明
class A {
public:
std::shared_ptr<B> b_ptr; // A 持有 B 的 shared_ptr
~A() { std::cout << "A destructed\n"; }
};
class B {
public:
std::shared_ptr<A> a_ptr; // B 持有 A 的 shared_ptr
~B() { std::cout << "B destructed\n"; }
};
int main() {
{
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b; // A 持有 B
b->a_ptr = a; // B 持有 A
std::cout << "Reference count of a: " << a.use_count() << "\n"; // 2
std::cout << "Reference count of b: " << b.use_count() << "\n"; // 2
} // 离开作用域,a 和 b 的引用计数仍为 1,不会析构
std::cout << "Scope exited\n";
return 0;
}
输出:
cpp
Reference count of a: 2
Reference count of b: 2
Scope exited
说明:
a
是main
中的std::shared_ptr<A>
,指向Object A
(引用计数 = 1)。b
是main
中的std::shared_ptr<B>
,指向Object B
(引用计数 = 1)。Object A
的成员b_ptr
(std::shared_ptr<B>
)指向Object B
,使Object B
的引用计数增至 2。Object B
的成员a_ptr
(std::shared_ptr<A>
)指向Object A
,使Object A
的引用计数增至 2。- 形成循环:
A -> B -> A
。
离开作用域时:
a
销毁,Object A
的引用计数从 2 减到 1(因为B
的a_ptr
仍在引用)。b
销毁,Object B
的引用计数从 2 减到 1(因为A
的b_ptr
仍在引用)。- 结果:
Object A
和Object B
的引用计数都不为 0,无法释放,内存泄漏。
图示:
通过将一方的 std::shared_ptr
替换为 std::weak_ptr
,可以打破循环引用。std::weak_ptr
引用资源但不增加引用计数,因此不会阻止资源释放。
cpp
#include <iostream>
#include <memory>
class B;
class A {
public:
std::shared_ptr<B> b_ptr;
~A() { std::cout << "A destructed\n"; }
};
class B {
public:
std::weak_ptr<A> a_ptr; // 使用 weak_ptr
~B() { std::cout << "B destructed\n"; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b; // A 持有 B
b->a_ptr = a; // B 弱引用 A
return 0;
}
输出:
css
B destructed
A destructed
说明:
a
指向Object A
(引用计数 = 1)。b
指向Object B
(引用计数 = 1)。Object A
的b_ptr
(std::shared_ptr<B>
)指向Object B
,Object B
的引用计数增至 2。Object B
的a_ptr
(std::weak_ptr<A>
)指向Object A
,但不增加Object A
的引用计数(仍为 1)。- 没有形成强引用循环,只有单向强引用:
A -> B
。
离开作用域时:
a
销毁,Object A
的引用计数从 1 减到 0,Object A
被释放,触发A
的析构函数。Object A
销毁时,其b_ptr
销毁,Object B
的引用计数从 2 减到 1。b
销毁,Object B
的引用计数从 1 减到 0,Object B
被释放,触发B
的析构函数。- 结果:资源正确释放,无内存泄漏。
图示:
std::weak_ptr
引用资源但不控制其生命周期(不增加引用计数)。当资源被所有 std::shared_ptr
释放后,std::weak_ptr
自动变为"过期"(expired),不会阻止资源析构。