系列文章目录
简介
shared_ptr
是一个类的模板,它使用引用计数的方式来管理一个动态分配的内存资源。shared_ptr
需要一个动态分配的对象时,会在堆上分配一块内存来存储该对象,并维护一个引用计数。
shared_ptr
允许多个shared_ptr
指向同一个对象,通过引用计数来跟踪有多少个shared_ptr
指向同一个对象。每次创建一个新的shared_ptr
指向同一个对象时,引用计数增加1。当一个shared_ptr
离开作用域或被重置时,引用计数减少1。当引用计数变为0时,shared_ptr
会自动删除所指向的对象并释放内存。
一、头文件
本文出现的关于shared_ptr
的方法都包含在此头文件中
#include <memory>
二、初始化
1. make_shared
最安全方法是调用make_shared
的标准库函数
// 指向一个值为默认值的int的shared_ptr:0
std::shared_ptr<int> p1 = make_shared<int>();
// 指向一个值为4的int的shared_ptr
std::shared_ptr<int> pt2 = make_shared<int>(4);
// 指向一个值为"555555"的string的shared_ptr
std::shared_ptr<std::string> p3= make_shared<string>(6, '5');
通常用定义一个auto
对象来保存make_shared
结果
// 指向一个值为默认值的int的shared_ptr:0
auto p4 = make_shared<int>();
2. 使用new关键字进行初始化
这种方式也会在堆上分配一个新的对象,并将指针赋值给共享智能指针。同样,会创建一个引用计数对象来追踪对象的引用计数。
std::shared_ptr<int> p2(new int(4))
3. 使用移动语义进行初始化
这种方式可以将一个共享智能指针的所有权转移给另一个共享智能指针,原来的指针将变为nullptr
。
std::shared_ptr<int> p3(new int(4));
std::shared_ptr<int> p4 = std::move(p3);
三、拷贝和赋值
通过拷贝和赋值也可以初始化。
auto p = make_shared<int>(4); // p指向的引用对象只有p一个引用者,此对象引用计数为1
auto q(p); // p和q指向的相同对象,此对象引用计数递增变为2
auto r = make_shared<int>(4); // r指向的引用对象只有r一个引用者,引用计数为1
auto r = q; // 给r赋值,令它指向另一个地址
// 递增q指向的对象的引用计数,此对象引用计数变为3
// 递减r原来指向的对象的引用计数,此引用计数变为0,自动释放
四、实例
#include <iostream>
#include <memory> // for std::shared_ptr and std::unique_ptr
class Person
{
public:
Person(std::string name, int age) :m_name(name), m_age(age)
{
std::cout << "Create Person" << " " << m_name.c_str() << " " << m_age << std::endl;
};
~Person()
{
std::cout << "Destroy Person" << " " << m_name.c_str() << " " << m_age << std::endl;
};
private:
std::string m_name;
int m_age;
};
int main()
{
// 离开作用域场合
{
// 创建shared_ptr
std::shared_ptr<Person> sharedPtr = std::make_shared<Person>("Tom", 10);
// 输出引用计数( 结果:1)
std::cout << "SharedPtr uses" << " " << sharedPtr.use_count() << std::endl;
{
// sharedPtr2和sharedPtr指向同一个对象,此对象的引用计数递增,变为2
std::shared_ptr<Person> sharedPtr2 = sharedPtr; // sharedPtr2不会调用构造函数,因为这个指针和sharedPtr指向的是同一个内存地址
// 输出引用计数( 结果:2)
std::cout << "SharedPtr uses" << " " << sharedPtr.use_count() << std::endl;
}
// 当sharedPtr2离开作用域时,其指向对象的引用计数递减,变为1(此时还有sharedPtr指向该对象)。
std::cout << "SharedPtr uses" << " " << sharedPtr.use_count() << std::endl;
}
// 当sharedPtr离开作用域时,引用计数递减变为0。然后析构函数自动被调用,析构函数会检查 shared_ptr 的引用计数,如果计数为 0,则调用 reset 函数来释放对象(不用显示调用),并将指针设为null。
std::cout << "Hello World!\n";
return 0;
}
五、总结
1.注意事项
- 避免循环引用:当存在循环引用时,即两个或多个对象相互持有对方的共享智能指针,可能会导致内存泄漏。为了避免这种情况,可以使用std::weak_ptr来打破循环引用。
- 不要使用裸指针来初始化共享智能指针:与独占智能指针类似,不要使用裸指针来初始化共享智能指针,以避免出现多个指针指向同一块内存的问题。
- 注意线程安全:共享智能指针的引用计数是线程安全的,但对象本身的访问需要额外的线程同步措施。
2. 使用场合
- 多个指针共享对象的所有权:当需要多个指针共享对象的所有权,并且在最后一个指针释放对象时才释放对象的内存时,可以使用共享智能指针。
- 在容器中管理动态分配的对象:与独占智能指针类似,共享智能指针也适用于在容器中管理动态分配的对象,避免手动释放内存。