type
status
slug
summary
tags
category
icon
password
new update day
Property
Oct 22, 2023 01:31 PM
created days
Last edited time
Oct 22, 2023 01:31 PM

什么是智能指针

智能指针是一种抽象的数据类型,它通常是经由类模板来实现,借由模板来达成泛型,借由类别的析构函数来达成自动释放指针所指向的存储器或对象。C++11 标准库提供了三种智能指针,分别是 std::shared_ptrstd::weak_ptrstd::unique_ptr ¹。
智能指针的优点在于可以避免忘记delete内存,避免内存泄露,同时可以大大减轻程序员在管理动态内存时的工作负担。尤其是当对象的析构时机比较难把握时(比如我们将对象指针作为函数返回值时)。有了智能指针,我们就只需要关注内存的申请,内存的释放则由程序自动完成 ¹。
不过,在某些场景下使用智能指针可能不太适合。例如,在高性能计算中,智能指针可能会带来一些额外的开销。此外,在某些情况下,使用智能指针也可能会导致循环引用问题。

1 shared_ptr

shared_ptr 是一种智能指针,它可以实现多个 shared_ptr 对象共享同一块内存。它的内部实现采用了引用计数机制,即每当有一个新的 shared_ptr 对象指向同一块内存时,该内存的引用计数会加 1 ;当一个 shared_ptr 对象被销毁时,它所指向的内存的引用计数会减 1 。当引用计数变为 0 时,即表明不再有其他 shared_ptr 对象与此内存关联,在这种情况下,该内存会被释放 ¹。
使用 shared_ptr 可以避免忘记释放内存而导致的内存泄漏问题。此外,由于 shared_ptr 内部采用了引用计数机制,即使有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放) ¹。

如何创建 shared_ptr

你可以通过多种方式创建 shared_ptr 对象。例如:
  1. 使用 std::make_shared 函数来创建 shared_ptr 对象:
  1. 直接使用 new 运算符来创建 shared_ptr 对象:
  1. 通过拷贝构造函数或赋值运算符来创建 shared_ptr 对象:

如何使用 shared_ptr

你可以像使用普通指针一样使用 shared_ptr。例如,你可以通过解引用运算符 * 来访问 shared_ptr 所指向的对象:
你也可以通过 -> 运算符来访问 shared_ptr 所指向对象的成员:
此外,shared_ptr 还提供了一些成员函数,例如 get() 函数可以返回 shared_ptr 所管理的原始指针,reset() 函数可以重置 shared_ptr 对象,而 use_count() 函数则可以返回当前 shared_ptr 对象的引用计数。

什么是循环引用问题?

循环引用问题是指两个或多个 shared_ptr 对象相互引用,导致它们的引用计数永远不会变为0,从而导致内存泄漏。例如:
在上面的例子中,sp1sp2 两个 shared_ptr 对象相互引用,因此它们的引用计数都不会变为 0,即使它们超出了作用域。这就导致了内存泄漏。

如何避免循环引用问题?

你可以使用 std::weak_ptr 来避免循环引用问题。weak_ptr 是一种弱引用,它不会增加 shared_ptr 的引用计数。当 shared_ptr 的引用计数变为0时,即使还有 weak_ptr 指向同一块内存,该内存也会被释放。
例如,我们可以修改前面的例子,使用 weak_ptr 来避免循环引用问题:
在上面的例子中,我们使用了 weak_ptr 来避免循环引用问题。当 sp1sp2 超出作用域时,它们所指向的内存会被正确释放。

如何使用 weak_ptr

你可以使用 std::weak_ptr 的构造函数或者 std::weak_ptr::lock() 函数来创建一个指向同一块内存的 shared_ptr 对象。例如:
在上面的例子中,我们首先创建了一个 shared_ptr 对象 sp,然后使用它来初始化一个 weak_ptr 对象 wp。接着,我们调用了 wp.lock() 函数来创建一个新的 shared_ptr 对象 sp2,它指向同一块内存。
注意,如果原始的 shared_ptr 对象已经被销毁,那么调用 wp.lock() 函数将返回一个空的 shared_ptr 对象。

详细介绍一下 unique_ptr

std::unique_ptr 是一种独占式智能指针,它保证同一时间只有一个 unique_ptr 对象可以指向同一块内存。当 unique_ptr 对象被销毁时,它所指向的内存也会被释放。
你可以使用 std::unique_ptr 的构造函数或者 std::make_unique 函数(C++14 起可用)来创建 unique_ptr 对象。例如:
在上面的例子中,我们分别使用了 std::unique_ptr 的构造函数和 std::make_unique 函数来创建两个 unique_ptr 对象。
注意,由于 unique_ptr 是独占式的,因此它不支持拷贝构造和拷贝赋值。但是你可以使用移动构造和移动赋值来转移 unique_ptr 的所有权:
在上面的例子中,我们首先使用移动构造函数来创建一个新的 unique_ptr 对象 up2,它接管了 up1 所指向的内存。然后我们又使用移动赋值运算符将 up2 的所有权转移回给了 up1

如何使用 unique_ptr

你可以像使用普通指针一样使用 unique_ptr。例如,你可以通过解引用运算符 * 来访问 unique_ptr 所指向的对象:
你也可以通过 -> 运算符来访问 unique_ptr 所指向对象的成员:
此外,unique_ptr 还提供了一些成员函数,例如 get() 函数可以返回 unique_ptr 所管理的原始指针,而 reset() 函数则可以重置 unique_ptr 对象。

参考资料

源: 与必应的对话, 2023/4/18 (1) C++11 shared_ptr智能指针(超级详细) - C语言中文网. http://c.biancheng.net/view/7898.html. (2) C++:共享指针shared_ptr的理解与应用 - 知乎. https://zhuanlan.zhihu.com/p/547647844. (3) C++智能指针:shared_ptr用法详解_Tonson_的博客-CSDN博客. https://blog.csdn.net/weixin_44444450/article/details/111075086. (1) 智能指针的C++实现 - 知乎. https://zhuanlan.zhihu.com/p/64543967. (2) 第007问:C++智能指针的作用及原理 - 知乎. https://zhuanlan.zhihu.com/p/563599084. (3) C++智能指针的底层实现原理 - CSDN博客. https://blog.csdn.net/ArtAndLife/article/details/120793343.
批量解压当前目录下以及子目录下的 tar.gz 文件C++ 中虚函数与纯虚函数在开发过程中的区别与作用