shared_ptr工作原理
- 引用计数:
shared_ptr
内部使用引用计数机制来管理所指向的对象。当创建一个 shared_ptr
指向某个对象时,引用计数初始化为1。每次通过拷贝构造函数或赋值运算符创建新的 shared_ptr
指向同一对象时,引用计数加1。当一个 shared_ptr
析构时,引用计数减1。当引用计数降为0时,shared_ptr
会自动释放所指向的对象。
- 控制块:
shared_ptr
通常会维护一个控制块,该控制块不仅包含引用计数,还可能包含其他信息,如删除器(用于自定义对象释放逻辑)和弱引用计数(用于 weak_ptr
)。
适用场景
- 动态分配对象的共享所有权:例如在一个图形渲染系统中,多个组件可能需要共享一个纹理对象。
#include <memory>
#include <iostream>
class Texture {
public:
Texture() { std::cout << "Texture created" << std::endl; }
~Texture() { std::cout << "Texture destroyed" << std::endl; }
};
void someFunction() {
std::shared_ptr<Texture> texture1 = std::make_shared<Texture>();
std::shared_ptr<Texture> texture2 = texture1;
// 此时texture1和texture2共享同一个Texture对象,引用计数为2
}
// 函数结束,texture1和texture2析构,引用计数降为0,Texture对象被释放
- 容器中存储动态对象:当在
std::vector
或 std::list
等容器中存储动态分配的对象时,shared_ptr
可以确保对象在容器销毁或元素移除时正确释放。
#include <memory>
#include <vector>
#include <iostream>
class MyClass {
public:
MyClass() { std::cout << "MyClass created" << std::endl; }
~MyClass() { std::cout << "MyClass destroyed" << std::endl; }
};
int main() {
std::vector<std::shared_ptr<MyClass>> vec;
vec.emplace_back(std::make_shared<MyClass>());
// 容器销毁时,MyClass对象会被正确释放
return 0;
}
可能遇到的问题及解决方案
- 循环引用
- 问题:当两个或多个
shared_ptr
相互引用,形成循环依赖时,会导致引用计数永远不会降为0,从而造成内存泄漏。
- 示例:
#include <memory>
#include <iostream>
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; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b = b;
b->a = a;
// a和b的引用计数都不会降为0,A和B对象不会被释放
return 0;
}
- 解决方案:使用
weak_ptr
打破循环。weak_ptr
不会增加引用计数,它可以观察 shared_ptr
所管理的对象,但不影响对象的生命周期。
#include <memory>
#include <iostream>
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; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b = b;
b->a = a;
// 离开作用域后,a和b的引用计数降为0,A和B对象被释放
return 0;
}
- 性能开销
- 问题:
shared_ptr
的引用计数操作和控制块的维护会带来一定的性能开销,特别是在频繁创建和销毁 shared_ptr
的场景下。
- 解决方案:在性能敏感的代码段,评估是否可以使用其他更轻量级的内存管理方式,如
unique_ptr
(适用于对象只有一个所有者的情况),或者手动管理内存。如果必须使用 shared_ptr
,可以尽量减少不必要的 shared_ptr
创建和赋值操作,以降低性能开销。