C++ 智能指针是管理动态分配内存的强大工具,旨在自动释放对象,有效防止内存泄漏,并简化资源管理。它们是标准库(<memory> 头文件)的一部分,核心思想是将内存所有权封装在对象中,利用析构函数自动释放内存。
1. std::unique_ptr (C++11)
- 核心概念:独占所有权。同一时间只有一个 unique_ptr 拥有并负责管理所指对象。
- 特性:
- 不可复制 (unique_ptr 的拷贝构造函数和拷贝赋值运算符被删除)。
- 可移动(通过 std::move),所有权随之转移。
- 轻量级,几乎无额外开销(通常与裸指针大小相同)。
- 支持自定义删除器(用于管理非内存资源,如文件句柄、套接字)。
- 使用场景:
- 管理单个动态分配对象或数组(std::unique_ptr<T[]>)
- 工厂函数返回资源。
- 作为类的成员变量,表示独占拥有的资源。
- 在容器中存储指向多态对象的指针。
2. std::shared_ptr (C++11)
-核心概念:共享所有权。多个 shared_ptr 可以同时拥有并管理同一个对象。对象在所有最后一个指向它的 shared_ptr 被销毁(或重置)时才被销毁。
- 实现机制:使用引用计数。每个被管理的对象都有一个关联的控制块(通常动态分配),其中包含:
- 指向被管理对象的指针。
- 强引用计数(shared_ptr 的数量)。
- 弱引用计数(weak_ptr 的数量,见下文)。
- 自定义删除器(如果提供)。
- 自定义分配器(如果提供)
- 特性:
- 可复制(增加引用计数)。
- 可移动(所有权转移,不增加也不减少引用计数)。
- 有额外开销(控制块大小通常为两个指针)。
- 支持自定义删除器和分配器。
- 线程安全:引用计数的增减是原子的,但访问所指对象本身仍需外部同步。
- 使用场景:
- 多个实体需要共享访问同一对象,且无法明确谁应该最后释放对象时。
- 缓存。
- 监听器/观察者列表。
- 复杂的数据结构(如图、树节点)。
3. std::weak_ptr (C++11)
- 核心概念:弱引用。weak_ptr 指向一个由 shared_ptr 管理的对象,但不拥有该对象,不影响其引用计数。
- 目的:解决 shared_ptr 循环引用问题(两个或多个对象通过 shared_ptr 互相持有,导致引用计数永不归零,内存泄漏)。weak_ptr 用于打破循环。
- 特性:
- 必须从 shared_ptr 或另一个 weak_ptr 构造/赋值。
- 不能直接访问所指对象(没有 operator* 或 operator->)。
- 需要使用 lock() 成员函数尝试获取一个临时的 shared_ptr 来安全访问对象。如果底层对象已被销毁,lock() 返回一个空的 shared_ptr。
- 可用来检查资源是否依然存在 (expired() 成员函数)。
- 使用场景:
- 打破 shared_ptr 的循环引用(例如,父对象持有子对象的 shared_ptr,子对象持有指向父对象的 weak_ptr)。
- 缓存:持有对可能已过期资源的弱引用,需要时尝试重新加载。
- 观察者模式:观察者持有对主题的弱引用,避免主题被观察者意外保持存活。
4. std::auto_ptr (C++98/03, 已废弃于 C++11, 移除于 C++17)
- 曾经目的:提供基本的独占所有权语义。
- 严重缺陷:
- 尝试复制时执行移动语义(所有权转移),留下源指针为 nullptr。这极其违反直觉且容易导致悬空指针和崩溃。
- 不能在标准容器(如 std::vector)中安全使用。
- 绝对不要在新代码中使用!用 std::unique_ptr 替代。
5、补充事宜
1. std::unique_ptr 成员函数
reset() 释放当前对象并接管新对象(或置空) ptr.reset(new T);
release() 放弃所有权,返回原始指针并将自身置空 T* raw = ptr.release();
get() 返回指向被管理对象的原始指针(不放弃所有权) T* raw = ptr.get();
swap() 交换两个 unique_ptr 的内容 ptr1.swap(ptr2);
operator bool 检查是否持有对象(非空) if (ptr) { ... }
operator-> 访问被管理对象的成员 ptr->doSomething();
operator* 解引用被管理对象 *ptr = value;
operator[] 仅用于数组特化版(unique_ptr<T[]>),访问数组元素 ptr[0] = 42;
2. std::shared_ptr 成员函数
reset() 释放当前对象并接管新对象(或置空),减少原引用计数 ptr.reset(new T);
get() 返回指向被管理对象的原始指针(不放弃所有权) T* raw = ptr.get();
swap() 交换两个 shared_ptr 的内容 ptr1.swap(ptr2);
use_count() 返回强引用计数(线程安全但仅限调试) long count = ptr.use_count();
unique() 检查是否独占对象(C++20 已弃用,用 use_count() == 1 替代) if (ptr.unique()) { ... }
operator bool 检查是否持有对象(非空) if (ptr) { ... }
operator-> 访问被管理对象的成员 ptr->doSomething();
operator* 解引用被管理对象 *ptr = value;
operator[] (C++17+) 用于数组特化版(shared_ptr<T[]>),访问数组元素 ptr[0] = 42;
3. std::weak_ptr 成员函数
lock() 核心函数:尝试提升为 shared_ptr,失败返回空 auto sp = wp.lock();
reset() 释放当前弱引用,置为空指针 wp.reset();
swap() 交换两个 weak_ptr 的内容 wp1.swap(wp2);
use_count() 返回关联的 shared_ptr 的强引用计数 long count = wp.use_count();
expired() 检查关联对象是否已被销毁(等价于 use_count() == 0) if (wp.expired()) { ... }
operator bool 检查是否关联对象(即使对象已销毁) if (wp) { ... } (不常用)
4.std::unique_ptr 的自定义删除器
//定义,基本语法
std::unique_ptr<T, Deleter> ptr(raw_ptr, deleter);T:指针指向的类型Deleter:删除器的类型raw_ptr:原始指针deleter:删除器实例
使用示例
示例 1:函数指针作为删除器
#include <iostream>
#include <memory>
#include <cstdio>
void file_deleter(FILE* file) {
if (file) {
std::cout << "Closing file\n";
fclose(file);
}
}
int main() {
// 使用函数指针作为删除器
std::unique_ptr<FILE, decltype(&file_deleter)> file(
fopen("test.txt", "w"),
file_deleter
);
if (file) {
fputs("Hello, World!", file.get());
}
// 文件会在 unique_ptr 析构时自动关闭
return 0;
}示例 2:Lambda 表达式作为删除器
#include <iostream>
#include <memory>
#include <cstdio>
int main() {
// 使用 Lambda 作为删除器
auto file_deleter = [](FILE* file) {
if (file) {
std::cout << "Closing file via lambda\n";
fclose(file);
}
};
std::unique_ptr<FILE, decltype(file_deleter)> file(
fopen("test.txt", "w"),
file_deleter
);
if (file) {
fputs("Hello, World!", file.get());
}
return 0;
}示例 3:函数对象作为删除器
#include <iostream>
#include <memory>
#include <cstdio>
struct FileDeleter {
void operator()(FILE* file) const {
if (file) {
std::cout << "Closing file via functor\n";
fclose(file);
}
}
};
int main() {
// 使用函数对象作为删除器
std::unique_ptr<FILE, FileDeleter> file(fopen("test.txt", "w"));
if (file) {
fputs("Hello, World!", file.get());
}
return 0;
}5.2. std::shared_ptr 的自定义删除器
std::shared_ptr 的自定义删除器使用更加简单,因为删除器的类型不是模板参数的一部分。
使用示例
#include <iostream>
#include <memory>
#include <cstdio>
int main() {
// 使用 Lambda 作为 shared_ptr 的删除器
auto file_deleter = [](FILE* file) {
if (file) {
std::cout << "Closing file via shared_ptr deleter\n";
fclose(file);
}
};
std::shared_ptr<FILE> file(fopen("test.txt", "w"), file_deleter);
if (file) {
fputs("Hello, World!", file.get());
}
return 0;
}