文章目录
共享指针是一种智能指针,它提供了与 std::unique_ptr 相似的语法,但是它允许多个指针共享同一对象的所有权。
当不再需要该对象时,共享指针会自动计数,并在没有任何指针指向该对象时释放它。
示例
#include <memory>
#include <string>
#include <iostream>
int main()
{
// 创建一个指向字符串的 shared_ptr
std::shared_ptr<std::string> event = std::make_shared<std::string>("This is an event."); //This is an event.
std::cout << *event << std::endl;
// 查看共享对象引用计数
std::cout << "event.use_count() = " << event.use_count() << std::endl; //event.use_count() = 1
// 多个 shared_ptr 可以指向同一个对象
std::shared_ptr<std::string> event2 = event;
std::cout << *event2 << std::endl; //This is an event.
// 查看共享对象引用计数
std::cout << "event.use_count() = " << event.use_count() << std::endl; //event.use_count() = 2
std::cout << "event2.use_count() = " << event2.use_count() << std::endl; //event2.use_count() = 2
// 对象只有当所有 shared_ptr 对它的引用都断开后,才会被销毁
event.reset();
// 查看共享对象引用计数
std::cout << "event.use_count() = " << event.use_count() << std::endl; //event.use_count() = 0 //如果共享指针已被销毁,查看其引用计数,不会报错,值为0,有点奇怪(下同)
std::cout << "event2.use_count() = " << event2.use_count() << std::endl; //event2.use_count() = 1
event2.reset();
std::cout << "event.use_count() = " << event.use_count() << std::endl; //event.use_count() = 0
std::cout << "event2.use_count() = " << event2.use_count() << std::endl; //event2.use_count() = 0
return 0;
}
上面代码中,我们通过 std::make_shared<std::string>("This is an event.")
创建了一个指向字符串的 shared_ptr
。然后我们复制这个指针到了 event2,现在两个指针都指向了同一个字符串。当我们在最后调用 event.reset()
和 event2.reset()
时,因为两个指针都不再引用这个字符串,所以这个字符串就会被销毁。
注意:如果共享指针已被reset(),查看其引用计数,不会报错,值为0,为何?因为调用reset()只是将共享指针指向置为nullptr,reset(std::shared_ptr.get())还可以将共享指针指向另一个共享指针所管理的内存
当一个 std::shared_ptr 对象调用 reset() 函数后,它所管理的对象的引用计数会减1,如果这个 std::shared_ptr 对象是最后一个指向该对象的 std::shared_ptr 对象,则这个对象所管理的资源会被销毁,引用计数会变为0。
当调用 reset() 函数后,这个 std::shared_ptr 对象本身会变为空(即不指向任何对象),而且其 use_count() 函数的返回值会变成0。因此,如果之后再调用这个被 reset() 后的 std::shared_ptr 对象的 use_count() 函数,其返回值仍然为0,因为这个 std::shared_ptr 对象已经不再指向任何对象了。
需要注意的是,在调用 reset() 函数后,共享指针对象会将自己的指针置为空,即不再指向任何对象。因此,如果之后调用这个共享指针对象的成员函数,如 use_count(),就会导致未定义行为。所以,为了避免这种情况发生,我们应该在调用 reset() 后避免对这个共享指针对象进行任何操作。。
测试代码
#include <memory>
#include <string>
#include <iostream>
#include <chrono>
#include <thread>
int main()
{
// 创建一个指向字符串的 shared_ptr
std::shared_ptr<std::string> event = std::make_shared<std::string>("This is an event."); //This is an event.
std::cout << *event << std::endl;
// 打印共享对象指向的资源地址
std::cout << "event.get() = " << event.get() << std::endl; //event.get() = 0x55f8cf40eec0
// 查看共享对象引用计数
std::cout << "event.use_count() = " << event.use_count() << std::endl; //event.use_count() = 1
// 对象只有当所有 shared_ptr 对它的引用都断开后,才会被销毁
event.reset(); //将共享指针指向置为nullptr
// 打印共享对象指向的资源地址
std::cout << "event.get() = " << event.get() << std::endl; //event.get() = 0 //指向资源地址为空指针nullptr
// 查看共享对象引用计数
std::cout << "event.use_count() = " << event.use_count() << std::endl; //event.use_count() = 0
return 0;
}
编译运行结果:
如何确认引用计数为0后,被共享指针管理的共享对象是否已销毁?
可以在共享对象的析构函数中设置标识,然后在确定引用计数为0后,检查这个标识以确定共享对象是否已销毁。
#include <iostream>
#include <memory>
class A
{
public:
A()
{
std::cout << "A Constructed" << std::endl;
}
~A()
{
std::cout << "A Destructed" << std::endl;
}
};
int main()
{
std::shared_ptr<A> pA(new A);
std::cout << "A's Reference Count: " << pA.use_count() << std::endl;
std::shared_ptr<A> pB = pA;
std::cout << "A's Reference Count: " << pA.use_count() << std::endl;
std::cout << "B's Reference Count: " << pB.use_count() << std::endl;
pA.reset();
std::cout << "A's Reference Count: " << pA.use_count() << std::endl;
std::cout << "B's Reference Count: " << pB.use_count() << std::endl;
pB.reset();
std::cout << "A's Reference Count: " << pA.use_count() << std::endl;
std::cout << "B's Reference Count: " << pB.use_count() << std::endl;
return 0;
}
编译运行:
g++ test.cpp && ./a.out
结果:
std::shared_ptr和std::unique_ptr有什么区别?
std::shared_ptr和std::unique_ptr是C++11中提供的智能指针类模板,它们都提供了一种安全且方便的方式来管理动态分配的内存。
主要区别:
-
所有权的管理方式不同。
std::unique_ptr是一种独占式智能指针,它对于所管理的对象具有唯一的所有权。这意味着一个std::unique_ptr指针不能与另一个指针共享所有权,也不能将其复制到另一个std::unique_ptr对象中。
std::shared_ptr是一种共享式智能指针,它允许多个指针共享对所管理的对象的所有权。当没有任何std::shared_ptr指针指向对象时,对象才会被自动销毁。 -
内存管理方式不同。
std::unique_ptr使用基于移动语义的所有权转移方式来管理内存。这意味着当一个std::unique_ptr对象被赋值或传递给另一个对象时,它所管理的对象的所有权将被转移到新的std::unique_ptr对象。
std::shared_ptr使用引用计数的内存管理方式来管理内存。每当一个新的std::shared_ptr指针指向一个对象时,该对象的引用计数会增加。当一个std::shared_ptr指针不再指向对象时,该对象的引用计数会减少。只有当对象的引用计数变为零时,该对象才会被自动销毁。 -
性能差异。
由于std::shared_ptr需要在堆上维护一个引用计数,因此它的构造和赋值操作比std::unique_ptr慢,并且它也可能涉及额外的线程同步开销。但是,std::shared_ptr可以方便地实现多个指针之间的共享所有权,因此在需要共享所有权的情况下,它可能是更好的选择。
总之,std::unique_ptr适用于独占式的所有权管理,对于无需共享所有权的情况,它通常是更好的选择。std::shared_ptr适用于需要共享所有权的情况,但由于额外的引用计数和线程同步开销,它可能会导致性能问题。