智能指针: shared_ptr
2023年12月28日 2023年12月30日
引用计数 reference count
shared_ptr的计数器
管理同一个动态对象的shared_ptr拥有相同的计数器, 该计数器指示了管理该动态对象的shared_ptr个数
计数器递增的情景
- 使用一个shared_ptr初始化另一个shared_ptr时
sp1的计数器递增, sp1和sp2共用计数器
1shared_ptr<T> sp2(sp1);
- 使用一个shared_ptr为另一个shared_ptr赋值时
sp2的计数器递增, sp1和sp2拥有相同的计数器, sp1和sp2指向相同对象
1sp1 = sp2;
认为管理该动态对象的shared_ptr增加
计数器递减的情景
- 为shared_ptr赋值
sp1的原计数器递减
1sp1 = sp2;
- 销毁shared_ptr时
1{ 2 sp; 3}
认为管理该动态对象的shared_ptr减少
如果计数器递减为0, 销毁shared_ptr管理的动态对象
多个shared_ptr联合管理一个动态对象; 最后一个shared_ptr被销毁时,销毁动态对象
shared_ptr作为函数的参数和返回值
作为函数的参数
需注意shared_ptr的生存期
1void process(shared_ptr<int> ptr) {} 2 3int *x(new int(1024)); 4process(shared_ptr<int>(x)); // 动态对象已被销毁 5int j = *x; // 错误: 访问非法内存
作为函数的返回类型
对于shared_ptr管理的动态对象,shared_ptr类保证只要有任何shared_ptr对象引用还在引用它,它就不会被销毁
1shared_ptr<Foo> factory(T arg) 2{ 3 return make_shared<Foo>(arg); // 使用arg构建动态对象,由shared_ptr管理动态对象:在适当的时候释放 4} 5 6void use_factory(T arg) 7{ 8 shared_ptr<Foo> p = factory(arg); // 由局部变量p管理内存 9} 10// 函数结束,将shared_ptr对象p销毁; 销毁p时,递减引用计数并检查其是否为0; 为0则对p指向的动态对象调用析构函数 11// 此例中, 销毁p管理的动态对象 12 13shared_ptr<Foo> use_factory(T arg) 14{ 15 shared_ptr<Foo> p = factory(arg); 16 return p; // 将p的拷贝作为返回值时, 会递增引用计数 17} 18// 函数结束, 销毁p, 而引用计数不为0; 此时, 动态对象还未被销毁
make_shared操作: 为shared_ptr申请动态内存
模板函数
返回一个shared_ptr对象
使用make_shared的参数匹配对象的构造函数; 参数列表为空时值初始化动态对象
1auto sp = make_shared<T>(); 2auto sp = make_shared<T>(args);
示例
1shared_ptr<int> p1 = make_shared<int>(); 2shared_ptr<int> p2 = make_shared<int>(42); 3auto p3 = make_shared<vector<string>>(); 4shared_ptr<string> p4 = make_shared<string>(10, '9'); 5 6cout << *p1 << endl; 7cout << *p2 << endl; 8cout << *p4 << endl;
use_count操作: 获取引用计数
返回联合管理该动态对象的shared_ptr个数
1sp.use_count();
unique操作: 判断是否有其他shared_ptr联合管理该动态对象
返回类型为bool
shared_ptr唯一返回true; 否则返回false
1p.unique();
多个shared_ptr联合管理动态对象
对shared_ptr对象执行拷贝和赋值操作时, 会更新动态对象的引用计数
使用一个shared_ptr初始化另一个shared_ptr
使用sp1初始化sp2
1shared_ptr<T> sp1 = make_shared<T>(args); // sp1指向动态对象A, 引用计数为X 2shared_ptr<T> sp2(sp1); // 递增动态对象A的引用计数, 为X+1; sp1和sp2共用引用计数
使用一个shared_ptr为另一个shared_ptr赋值
使用sp2为sp1赋值
1sp1 = sp2;
第一种说明方法, 分情况讨论:
- sp1和sp2均为空
- sp1为空; sp2指向动态对象B, 引用计数为Y
递增Y, sp1和sp2共用引用计数 - sp1指向动态对象A, 引用计数为X; sp2为空
递减X: 如果X变作0, 销毁A - sp1指向动态对象A, 引用计数为X; sp2指向动态对象B, 引用计数为Y
递减X: 如果X变作0, 销毁A
递增Y, sp1和sp2共用引用计数
第二种说明方法:
- sp2指向动态对象B: 如果B存在,递增引用计数Y
- sp1指向动态对象A: 如果A存在, 递减引用计数X; 如果X变作0, 对A调用析构函数
shared_ptr作为函数的参数和返回值
创建shared_ptr: 使用移后源初始化
1shared_ptr<T> sp2(std::move(sp1));
sp1被置空
示例
仅供参考
1#include <iostream> 2#include <ostream> 3#include <string> 4#include <vector> 5#include <memory> 6 7using namespace std; 8 9class Foo 10{ 11 friend ostream &operator<<(ostream &os, const Foo &f); 12 13public: 14 Foo(string _name) : name(_name) {} 15 ~Foo() { cout << "Call ~Foo for " << *this << endl << endl; } 16 17private: 18 string name; 19}; 20 21ostream &operator<<(ostream &os, const Foo &f) 22{ 23 os << f.name; 24 return os; 25} 26 27void DeleteFoo(Foo *f) 28{ 29 cout << "call " << __FUNCTION__ << " for " << *f << endl; 30 delete f; // 调用~Foo() 31} 32 33int main() 34{ 35 auto p = new Foo("Peter"); 36 37 shared_ptr<Foo> sp1(p), sp2(sp1); 38 39 cout << "*sp2 equals " << *sp2 << endl; 40 41 shared_ptr<Foo> sp3(std::move(sp1)); 42 43 if (sp1 == nullptr) cout << "sp1 is null.\n"; 44 if (sp2 == sp3) cout << "equal.\n"; 45 46 cout << sp2.use_count() << "\t" << sp3.use_count() << endl; 47 48 cout << "main finished.\n"; 49 return 0; 50}
输出
*sp2 equals Peter sp1 is null. equal. 2 2 main finished. Call ~Foo for Peter