智能指针

内存泄漏分析工具:valgrind

std::unique_ptr

std::unique_ptr是C++11标准引入的智能指针之一,用于独占式管理单个动态分配的对象。它提供了自动内存管理的功能,确保当unique_ptr离开作用域时,它所管理的对象会被自动销毁,符合==RAII原则==

特性

  1. 独占所有权:std::unique_ptr拥有其指向的对象的独占所有权。这意味着在任何给定时间,只有一个unique_ptr可以管理一个给定的动态分配对象
  2. 不可复制:为了防止所有权的歧义,std::unique_ptr不支持拷贝操作。这避免了多个指针指向同一资源时可能出现的资源管理问题
  3. 可移动:std::unique_ptr支持移动语义,这意味着你可以将一个unique_ptr的所有权转移给另一个unique_ptr。移动后,原始的unique_ptr将不再拥有任何对象
  4. 析构时自动释放资源:当unique_ptr的实例被销毁时,它会自动释放所管理的资源(即删除它所指向的对象)
  5. 自定义删除器:std::unique_ptr允许你指定一个自定义的删除器,这可以用来管理非默认的资源释放逻辑
  6. 该类重载了=操作符,如果对于一个std::unique_ptr重新赋值,那么先前分配的资源会被直接释放
  7. 该类由于独占性,所以一般不当函数的形参,除非使用移动语义来转移资源的所有权

语法

管理单个对象:

1
2
3
4
5
6
7
8
void function() 
{
// 创建一个unique_ptr,管理一个动态分配的整数
std::unique_ptr<int> ptr(new int(10));
std::unique_ptr<int> ptr = std::make_unique<int>(10); //工厂方法的创建对象
std::cout << *ptr << std::endl; // 输出: 10
// 当function结束时,ptr被销毁,它所管理的整数也被自动删除
}

管理数组:

1
2
3
4
5
{
std::unique_ptr<int[]> arr(new int[5]{1, 2, 3, 4, 5});
std::unique_ptr<double[]> a = std::make_unique<double[]>(5); //数组大小为5
std::unique_ptr<double[]> b = std::make_unique<double[]>(5,10); //数组大小为5,初值全为10
}

std::shared_ptr

该智能指针用于管理动态分配的内存,并且允许多个shared_ptr实例共享对同一资源的所有权

特性

  1. 共享所有权std::shared_ptr允许多个指针实例共享对同一个对象的所有权。这是通过内部的引用计数机制实现的,当最后一个shared_ptr被销毁或被重置时,对象会被自动删除。
  2. 自动内存管理:由于引用计数的存在,std::shared_ptr能够自动管理内存,减少内存泄漏的风险。
  3. 异常安全:即使在构造或赋值过程中发生异常,std::shared_ptr也能确保资源的正确释放。
  4. 复制和赋值std::shared_ptr可以被复制和赋值,每次复制都会增加内部引用计数。
  5. 可空性std::shared_ptr可以存储空指针,这使得它在某些情况下可以作为普通指针的替代品。

注意事项

  • 循环引用问题:如果两个或多个对象相互持有对方的引用,这将导致引用计数永远不会达到零,从而产生内存泄漏。这可以通过使用std::weak_ptr来解决
  • std::shared_ptr的创建和复制比裸指针稍贵,因为它们需要维护引用计数

语法

std::shared_ptr作为函数的形参:值传递、引用传递、指针传递都行,推荐直接值传递,这样比较简单

1
2
3
4
5
6
7
void process(std::unique_ptr<int> ptr);
void modify(std::unique_ptr<int>& ptr);
void display(const std::unique_ptr<int>* ptr);
void lock_weak(std::weak_ptr<int> weak) {
if(weak.lock())
{...}// 尝试获取shared_ptr,如果对象不存在则返回空
}

std::weak_ptr

std::weak_ptr是C++标准库中的智能指针,它提供了一种不拥有对象所有权的观察方式,通常与std::shared_ptr配合使用

特性

  1. 弱拥有权std::weak_ptr不拥有它所指向的对象的所有权。它只是观察由std::shared_ptr管理的对象,并不增加对象的引用计数
  2. 解决循环引用std::weak_ptr常用于解决std::shared_ptr之间可能形成的循环引用问题
  3. 延迟访问std::weak_ptr可以延迟对对象的访问,直到确定对象仍然有效
  4. **转换为shared_ptr**:std::weak_ptr可以被转换为std::shared_ptr,如果对象仍然存活。如果对象已经被销毁,转换将返回一个空的shared_ptr
  5. 自动重置:当std::weak_ptr观察的对象被销毁时,std::weak_ptr会自动变为空

语法

创建std::weak_ptr

1
2
3
4
5
6
7
#include <memory>
#include <iostream>

int main() {
std::shared_ptr<int> shared(new int(10));
std::weak_ptr<int> weak(shared); // 创建一个weak_ptr,观察由shared_ptr管理的对象
}

访问对象

1
2
3
4
if (weak.lock() != nullptr) {
// weak.lock()返回一个shared_ptr,如果对象仍然存活
std::cout << *weak.lock() << std::endl;
}

检查是否为空

1
2
3
4
5
if (weak.expired()) {
// 对象已经被销毁
} else {
// 对象仍然存活
}