type
Post
status
Published
slug
2023/04/18/ptr-in-c++
summary
C++ 中的智能指针,具体的实现原理,同时使用它的优点,以及使用它的场景,有哪些场景不适合使用它
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_ptr
,std::weak_ptr
和 std::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
对象。例如:- 使用
std::make_shared
函数来创建shared_ptr
对象:
auto sp = std::make_shared<int>(42);
- 直接使用
new
运算符来创建shared_ptr
对象:
std::shared_ptr<int> sp(new int(42));
- 通过拷贝构造函数或赋值运算符来创建
shared_ptr
对象:
auto sp1 = std::make_shared<int>(42); auto sp2 = sp1; // 拷贝构造 auto sp3 = std::shared_ptr<int>(); sp3 = sp1; // 赋值
如何使用 shared_ptr
你可以像使用普通指针一样使用
shared_ptr
。例如,你可以通过解引用运算符 *
来访问 shared_ptr
所指向的对象:auto sp = std::make_shared<int>(42); std::cout << *sp << std::endl; // 输出 42
你也可以通过
->
运算符来访问 shared_ptr
所指向对象的成员:struct Foo { int x; int y; }; auto sp = std::make_shared<Foo>(); sp->x = 10; sp->y = 20;
此外,
shared_ptr
还提供了一些成员函数,例如 get()
函数可以返回 shared_ptr
所管理的原始指针,reset()
函数可以重置 shared_ptr
对象,而 use_count()
函数则可以返回当前 shared_ptr
对象的引用计数。什么是循环引用问题?
循环引用问题是指两个或多个
shared_ptr
对象相互引用,导致它们的引用计数永远不会变为0,从而导致内存泄漏。例如:struct Foo { std::shared_ptr<Foo> other; }; auto sp1 = std::make_shared<Foo>(); auto sp2 = std::make_shared<Foo>(); sp1->other = sp2; sp2->other = sp1;
在上面的例子中,
sp1
和 sp2
两个 shared_ptr
对象相互引用,因此它们的引用计数都不会变为 0,即使它们超出了作用域。这就导致了内存泄漏。如何避免循环引用问题?
你可以使用
std::weak_ptr
来避免循环引用问题。weak_ptr
是一种弱引用,它不会增加 shared_ptr
的引用计数。当 shared_ptr
的引用计数变为0时,即使还有 weak_ptr
指向同一块内存,该内存也会被释放。例如,我们可以修改前面的例子,使用
weak_ptr
来避免循环引用问题:struct Foo { std::weak_ptr<Foo> other; }; auto sp1 = std::make_shared<Foo>(); auto sp2 = std::make_shared<Foo>(); sp1->other = sp2; sp2->other = sp1;
在上面的例子中,我们使用了
weak_ptr
来避免循环引用问题。当 sp1
和 sp2
超出作用域时,它们所指向的内存会被正确释放。如何使用 weak_ptr
你可以使用
std::weak_ptr
的构造函数或者 std::weak_ptr::lock()
函数来创建一个指向同一块内存的 shared_ptr
对象。例如:auto sp = std::make_shared<int>(42); std::weak_ptr<int> wp(sp); auto sp2 = wp.lock();
在上面的例子中,我们首先创建了一个
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
对象。例如:auto up1 = std::unique_ptr<int>(new int(42)); auto up2 = std::make_unique<int>(42); // C++14 起可用
在上面的例子中,我们分别使用了
std::unique_ptr
的构造函数和 std::make_unique
函数来创建两个 unique_ptr
对象。注意,由于
unique_ptr
是独占式的,因此它不支持拷贝构造和拷贝赋值。但是你可以使用移动构造和移动赋值来转移 unique_ptr
的所有权:auto up1 = std::make_unique<int>(42); auto up2 = std::move(up1); // 移动构造 up1 = std::move(up2); // 移动赋值
在上面的例子中,我们首先使用移动构造函数来创建一个新的
unique_ptr
对象 up2
,它接管了 up1
所指向的内存。然后我们又使用移动赋值运算符将 up2
的所有权转移回给了 up1
。如何使用 unique_ptr
你可以像使用普通指针一样使用
unique_ptr
。例如,你可以通过解引用运算符 *
来访问 unique_ptr
所指向的对象:auto up = std::make_unique<int>(42); std::cout << *up << std::endl; // 输出 42
你也可以通过
->
运算符来访问 unique_ptr
所指向对象的成员:struct Foo { int x; int y; }; auto up = std::make_unique<Foo>(); up->x = 10; up->y = 20;
此外,
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.
欢迎加入“喵星计算机技术研究院”,原创技术文章第一时间推送。
- 作者:tangcuyu
- 链接:https://expoli.tech/articles/2023/04/18/ptr-in-c%2B%2B
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章