
使用std::shared_ptr管理内存时,需通过正确处理引用计数(如避免循环引用,必要时结合std::weak_ptr或自定义删除器),确保对象在无引用时被及时销毁,从而避免内存泄漏。
std::shared_ptr的核心是引用计数机制:当创建一个shared_ptr时,会为对象分配一个引用计数(默认为1),每次拷贝或赋值shared_ptr时,引用计数加1;当shared_ptr被销毁或指向对象被reset时,引用计数减1。当引用计数降为0时,对象会被调用其deleter(默认为delete)销毁。
循环引用问题出现在两个或多个shared_ptr互相持有对方,导致每个对象的引用计数始终大于1,即使外部不再持有,对象也不会被销毁。类比:就像一个房间有多个房客,每个房客都持有房间的钥匙,房间永远不会空,因为每个房客都认为房间还有人住,但实际上没人离开。
| 对比点 | std::shared_ptr | std::weak_ptr | 解决循环引用方案 |
|---|---|---|---|
| 定义 | 管理动态分配的指针,自动释放内存 | 不管理对象,用于解决shared_ptr的循环引用 | 结合weak_ptr或自定义deleter |
| 引用计数 | 有,管理对象生命周期 | 无,仅提供对shared_ptr的临时访问 | 通过weak_ptr检查是否有效 |
| 使用场景 | 独占或共享对象,需要自动管理内存 | 避免循环引用,临时访问对象 | 当对象间有循环依赖时(如A持有B的weak_ptr,B持有A的shared_ptr) |
循环引用问题(错误示例):
class B;
class A {
public:
std::shared_ptr<B> b_ptr;
A() : b_ptr(std::make_shared<B>(this)) {} // A持有B,B持有A
};
class B {
public:
std::shared_ptr<A> a_ptr;
B(std::shared_ptr<A> a) : a_ptr(a) {} // B持有A
};
解决方案(使用weak_ptr):
class A2 {
public:
std::weak_ptr<B2> b_ptr;
A2() : b_ptr(std::make_shared<B2>()) {}
};
class B2 {
public:
std::shared_ptr<A2> a_ptr;
B2(std::shared_ptr<A2> a) : a_ptr(a) {}
};
面试官您好,关于使用std::shared_ptr避免内存泄漏,核心是正确管理引用计数。首先,std::shared_ptr通过引用计数机制自动管理对象生命周期,当引用计数为0时释放内存。但循环引用会导致两个shared_ptr互相持有,引用计数永远不会为0,此时需要用std::weak_ptr临时访问对象,避免循环。比如,当A持有B的weak_ptr,B持有A的shared_ptr,这样A的引用计数不会因为B的持有而增加,最终A的引用计数为0时会被释放。另外,如果自定义删除器(如管理文件句柄),需确保deleter正确执行,避免资源泄漏。总结来说,避免内存泄漏的关键是正确处理引用计数,特别是循环引用场景,通过weak_ptr或自定义deleter解决。
问:weak_ptr的expired()方法如何使用?
答:expired()检查shared_ptr是否有效,若返回true则对象已被销毁,此时weak_ptr指向的原始指针可能无效。
问:自定义deleter的作用?
答:用于自定义资源释放方式(如管理文件、网络连接),避免默认delete不适用的情况。
问:unique_ptr和shared_ptr的区别?
答:unique_ptr独占对象(不能拷贝),用于避免共享;shared_ptr共享(支持拷贝),用于自动管理内存,但需注意循环引用问题。
问:除了循环引用,还有哪些内存泄漏原因?
答:比如忘记reset,或shared_ptr的deleter错误导致对象未被释放。