文章来源:https://www.zhihu.com/question/319277442/answer/1517987598
std::unique_ptr 对其持有的堆内存具有唯一拥有权,也就是说引用计数永远是 1,std::unique_ptr 对象销毁时会释放其持有的堆内存。
可以使用以下方式初始化一个 std::unique_ptr 对象:
//初始化方式1
std::unique_ptr<int> sp1(new int(123));
//初始化方式2
std::unique_ptr<int> sp2;
sp2.reset(new int(123));
//初始化方式3
std::unique_ptr<int> sp3 = std::make_unique<int>(123); // C++14支持
你应该尽量使用初始化方式 3 的方式去创建一个 std::unique_ptr 而不是方式 1 和 2,因为形式 3 更安全,原因 Scott Meyers 在其《Effective Modern C++》中已经解释过了,有兴趣的读者可以阅读此书相关章节。
auto delete_Investment = [](Investment* pInv)
{
pInv->getObjectType();
delete pInv;
};
unique_ptr<Investment,decltype(delete_Investment)> pInvest(nullptr,delete_Investment);
或者也可以使用函数指针
void deleteInv(Investment* pInv) {}
std::unique_ptr<Investment,void(*)(Investment*)>ptr(nullptr,deleteInv) ;
std::unique_ptr 对其持有的资源具有独占性,而 std::shared_ptr 持有的资源可以在多个 std::shared_ptr 之间共享,每多一个 std::shared_ptr 对资源的引用,资源引用计数将增加 1,每一个指向该资源的 std::shared_ptr 对象析构时,资源引用计数减 1,最后一个 std::shared_ptr 对象析构时,发现资源计数为 0,将释放其持有的资源。多个线程之间,递增和减少资源的引用计数是安全的。(注意:这不意味着多个线程同时操作 std::shared_ptr 引用的对象是安全的)。std::shared_ptr 提供了一个 use_count() 方法来获取当前持有资源的引用计数。除了上面描述的,std::shared_ptr 用法和 std::unique_ptr 基本相同。
下面是一个初始化 std::shared_ptr 的示例:
//初始化方式1
std::shared_ptr<int> sp1(new int(123));
//初始化方式2
std::shared_ptr<int> sp2;
sp2.reset(new int(123));
//初始化方式3
std::shared_ptr<int> sp3;
sp3 = std::make_shared<int>(123);
和 std::unique_ptr 一样,你应该优先使用 std::make_shared 去初始化一个 std::shared_ptr 对象。
实际开发中,有时候需要在类中返回包裹当前对象(this)的一个 std::shared_ptr 对象给外部使用,C++ 新标准也为我们考虑到了这一点,有如此需求的类只要继承自 std::enable_shared_from_this 模板对象即可。用法如下:
#include <iostream>
#include <memory>
class A : public std::enable_shared_from_this<A>
{
public:
A()
{
std::cout << "A constructor" << std::endl;
}
~A()
{
std::cout << "A destructor" << std::endl;
}
std::shared_ptr<A> getSelf()
{
return shared_from_this();
}
};
int main()
{
std::shared_ptr<A> sp1(new A());
std::shared_ptr<A> sp2 = sp1->getSelf();
std::cout << "use count: " << sp1.use_count() << std::endl;
return 0;
}
上述代码中,类 A 的继承 std::enable_shared_from_this 并提供一个 getSelf() 方法返回自身的 std::shared_ptr 对象,在 getSelf() 中调用 shared_from_this() 即可。
std::enable_shared_from_this 用起来比较方便,但是也存在很多不易察觉的陷阱。
陷阱一:不应该共享栈对象的 this 给智能指针对象
陷阱二:避免 std::enable_shared_from_this 的循环引用问题
std::weak_ptr 是一个不控制资源生命周期的智能指针,是对对象的一种弱引用,只是提供了对其管理的资源的一个访问手段,引入它的目的为协助 std::shared_ptr 工作。
std::weak_ptr 可以从一个 std::shared_ptr 或另一个 std::weak_ptr 对象构造,std::shared_ptr 可以直接赋值给 std::weak_ptr ,也可以通过 std::weak_ptr 的 lock() 函数来获得 std::shared_ptr。它的构造和析构不会引起引用计数的增加或减少。std::weak_ptr 可用来解决 std::shared_ptr 相互引用时的死锁问题(即两个std::shared_ptr 相互引用,那么这两个指针的引用计数永远不可能下降为 0, 资源永远不会释放)。
示例代码如下:
#include <iostream>
#include <memory>
int main()
{
//创建一个std::shared_ptr对象
std::shared_ptr<int> sp1(new int(123));
std::cout << "use count: " << sp1.use_count() << std::endl;
//通过构造函数得到一个std::weak_ptr对象
std::weak_ptr<int> sp2(sp1);
std::cout << "use count: " << sp1.use_count() << std::endl;
//通过赋值运算符得到一个std::weak_ptr对象
std::weak_ptr<int> sp3 = sp1;
std::cout << "use count: " << sp1.use_count() << std::endl;
//通过一个std::weak_ptr对象得到另外一个std::weak_ptr对象
std::weak_ptr<int> sp4 = sp2;
std::cout << "use count: " << sp1.use_count() << std::endl;
return 0;
// 执行结果如下:
//use count: 1
//use count: 1
//use count: 1
//use count: 1
}
无论通过何种方式创建 std::weak_ptr 都不会增加资源的引用计数,因此每次输出引用计数的值都是 1。
既然,std::weak_ptr 不管理对象的生命周期,那么其引用的对象可能在某个时刻被销毁了,如何得知呢?std::weak_ptr 提供了一个 expired() 方法来做这一项检测,返回 true,说明其引用的资源已经不存在了;返回 false,说明该资源仍然存在,这个时候可以使用 std::weak_ptr 的 lock() 方法得到一个 std::shared_ptr 对象然后继续操作资源,以下代码演示了该用法:
//tmpConn_ 是一个 std::weak_ptr<TcpConnection> 对象
//tmpConn_引用的TcpConnection已经销毁,直接返回
if (tmpConn_.expired())
return;
std::shared_ptr<TcpConnection> conn = tmpConn_.lock();
if (conn)
{
//对conn进行操作,省略...
}
有读者可能对上述代码产生疑问,既然使用了 std::weak_ptr 的 expired() 方法判断了对象是否存在,为什么不直接使用 std::weak_ptr 对象对引用资源进行操作呢?实际上这是行不通的,std::weak_ptr 类没有重写 operator-> 和 operator 方法,因此不能像 std::shared_ptr 或 std::unique_ptr 一样直接操作对象,同时 std::weak_ptr 类也没有重写 operator! 操作,因此也不能通过 std::weak_ptr* 对象直接判断其引用的资源是否存在:
#include <memory>
class A
{
public:
void doSomething()
{
}
};
int main()
{
std::shared_ptr<A> sp1(new A());
std::weak_ptr<A> sp2(sp1);
//正确代码
if (sp1)
{
//正确代码
sp1->doSomething();
(*sp1).doSomething();
}
//正确代码
if (!sp1)
{
}
//错误代码,无法编译通过
//if (sp2)
//{
// //错误代码,无法编译通过
// sp2->doSomething();
// (*sp2).doSomething();
//}
//错误代码,无法编译通过
//if (!sp2)
//{
//}
return 0;
}