1. 智能指针
- 智能指针是一种用于管理动态分配的内存的工具,能够帮助避免内存泄漏和悬挂指针等问题。
- 智能指针是C++11标准引入的一个重要特性,它们基于RAII(资源获取即初始化)原则,利用对象生命周期的概念,在对象生命周期结束时自动释放资源。
- 智能指针实际上是一个类对象,它封装了原始指针,并在其生命周期结束时负责释放指针所指向的内存。
- 智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。
- C++11引入了三种指针:
std::shared_ptr
、std::unique_ptr
、std::weak_ptr
2. std::shared_ptr
共享指针,允许多个指针指向同一块内存,它使用引用计数来跟踪有多少个指针指向了该对象。并且会在最后一个指针不再指向该内存区域时自动释放资源。
1 |
|
创建shared_ptr
使用
std::make_shared
函数来创建std::shared_ptr
,它会自动分配内存并构造对象,同时返回一个指向该对象的std::shared_ptr
。拷贝和赋值
将一个
std::shared_ptr
赋值给另一个时,引用计数会增加。当所有指向该对象的std::shared_ptr
都被销毁时,引用计数会减少。只有当引用计数为0时,资源才会被释放。引用计数
std::shared_ptr
内部维护了一个引用计数器,用于跟踪有多少个std::shared_ptr
指向了相同的资源。可以通过use_count()
方法获取引用计数。
3. std::weak_ptr
循环引用指的是两个或多个对象彼此之间相互引用,导致它们的引用计数永远不会变为零,从而造成内存泄漏。在使用std::shared_ptr
时,循环引用是一个常见的问题,因为std::shared_ptr
的引用计数机制可能导致对象永远无法被释放。如以下示例:
1 |
|
在这个例子中,a1
和a2
相互引用,即a1
的next
指针指向a2
,而a2
的next
指针又指向a1
。这样一来,它们之间的引用计数永远不会变为零,因为彼此都在互相引用。即使在main
函数结束时,a1
和a2
的引用计数也不会为零,导致A
类对象永远无法被销毁,造成内存泄漏。
std::weak_ptr
是std::shared_ptr
的一种弱引用,它不会增加引用计数。用于解决std::shared_ptr
的循环引用带来的内存泄漏问题。可以通过lock()
方法获得一个std::shared_ptr
,如果原来的std::shared_ptr
已经被销毁,则返回一个空指针。
上述示例中,可以将next
成员改为std::weak_ptr
类型,这样就不会导致循环引用。
1 |
|
在这个修改后的示例中,A
类的next
成员现在是std::weak_ptr
类型,这意味着a1->next
和a2->next
不会增加A
对象的引用计数。因此,即使a1
和a2
相互引用,它们之间的循环引用也被打破了。这样在main
函数结束时,A
类对象的引用计数会变为零,对象会被正确地销毁,从而避免了内存泄漏问题。
4. std::unique_ptr
std::unique_ptr
是一种独占所有权的智能指针,它确保在其生命周期内,只有一个指针可以指向该对象。当std::unique_ptr
被销毁时,它所管理的对象也会被自动释放。
- 创建unique_ptr
1 |
|
- 移动语义
std::unique_ptr
是独占所有权的,因此它不支持拷贝语义,但支持移动语义。这意味着可以通过移动操作将资源所有权从一个std::unique_ptr
转移到另一个。
1 |
|
- 释放资源
当std::unique_ptr
超出作用域时,它所管理的资源会被自动释放。
1 |
|
- 自定义删除器
std::unique_ptr
支持自定义删除器,可以指定在释放资源时调用的函数或者函数对象。
1 | #include <memory> |