面试题答案
一键面试展示循环引用情况的代码
#include <iostream>
#include <memory>
class B;
class A {
public:
std::shared_ptr<B> b;
~A() {
std::cout << "A destroyed" << std::endl;
}
};
class B {
public:
std::shared_ptr<A> a;
~B() {
std::cout << "B destroyed" << std::endl;
}
};
void testCycle() {
std::shared_ptr<A> ptrA = std::make_shared<A>();
std::shared_ptr<B> ptrB = std::make_shared<B>();
ptrA->b = ptrB;
ptrB->a = ptrA;
}
在上述代码中,testCycle
函数创建了A
和B
的shared_ptr
实例,并互相持有对方的shared_ptr
,这就形成了循环引用。当函数结束时,ptrA
和ptrB
的引用计数不会归零,A
和B
的析构函数不会被调用,造成内存泄漏。
使用std::weak_ptr
解决循环引用问题的代码
#include <iostream>
#include <memory>
class B;
class A {
public:
std::weak_ptr<B> b;
~A() {
std::cout << "A destroyed" << std::endl;
}
};
class B {
public:
std::weak_ptr<A> a;
~B() {
std::cout << "B destroyed" << std::endl;
}
};
void testNoCycle() {
std::shared_ptr<A> ptrA = std::make_shared<A>();
std::shared_ptr<B> ptrB = std::make_shared<B>();
ptrA->b = ptrB;
ptrB->a = ptrA;
}
在这个改进版本中,A
类和B
类中的成员变量改为std::weak_ptr
类型。weak_ptr
不会增加对象的引用计数,这样当testNoCycle
函数结束时,ptrA
和ptrB
的引用计数会归零,A
和B
的析构函数会被调用,避免了循环引用导致的内存泄漏。如果需要通过weak_ptr
访问对象,可以使用lock
方法,例如:
void accessObject() {
std::shared_ptr<A> ptrA = std::make_shared<A>();
std::shared_ptr<B> ptrB = std::make_shared<B>();
ptrA->b = ptrB;
ptrB->a = ptrA;
std::shared_ptr<B> lockedB = ptrA->b.lock();
if (lockedB) {
// 可以安全地使用lockedB
}
}