名称:cpp-smart-pointers 用户可调用:false 描述:用于在C++中使用智能指针,包括unique_ptr、shared_ptr和weak_ptr,以遵循RAII原则进行自动内存管理。 允许工具:
- 读取
- 写入
- 编辑
- 搜索
- 全局查找
- Bash
C++ 智能指针
智能指针通过RAII(资源获取即初始化)提供自动内存管理,消除手动调用new和delete的需要。它们防止内存泄漏、悬空指针和双重释放错误,同时清晰地表达所有权语义。
RAII 原则
RAII将资源生命周期绑定到对象生命周期,确保对象超出作用域时自动清理。
#include <memory>
#include <iostream>
#include <fstream>
// 文件句柄的RAII包装器
class FileHandle {
std::unique_ptr<std::FILE, decltype(&std::fclose)> file_;
public:
FileHandle(const char* filename, const char* mode)
: file_(std::fopen(filename, mode), &std::fclose) {
if (!file_) {
throw std::runtime_error("打开文件失败");
}
}
std::FILE* get() { return file_.get(); }
// 无需显式析构函数 - RAII处理清理
};
// 传统方法(易出错)
void manual_memory() {
int* ptr = new int(42);
// 如果这里抛出异常,内存泄漏!
delete ptr;
}
// RAII方法(安全)
void raii_memory() {
auto ptr = std::make_unique<int>(42);
// 即使抛出异常也自动清理
}
// 多资源的RAII
void multiple_resources() {
auto file1 = std::make_unique<FileHandle>("data.txt", "r");
auto file2 = std::make_unique<FileHandle>("output.txt", "w");
// 两个文件按反向顺序自动关闭
}
unique_ptr - 独占所有权
unique_ptr表示独占所有权,零运行时开销,并仅支持移动语义。
#include <memory>
#include <vector>
#include <iostream>
class Widget {
int id_;
public:
Widget(int id) : id_(id) {
std::cout << "Widget " << id_ << " 创建
";
}
~Widget() {
std::cout << "Widget " << id_ << " 销毁
";
}
int id() const { return id_; }
};
void unique_ptr_basics() {
// 创建unique_ptr
std::unique_ptr<Widget> w1(new Widget(1));
auto w2 = std::make_unique<Widget>(2); // 推荐
// 访问成员
std::cout << "Widget ID: " << w2->id() << "
";
// 释放所有权
Widget* raw = w2.release();
delete raw; // 现在需负责
// 重置为新对象
w1.reset(new Widget(3)); // 旧widget销毁
// 获取原始指针(保留所有权)
Widget* ptr = w1.get();
// 移动所有权(unique_ptr仅可移动)
std::unique_ptr<Widget> w3 = std::move(w1);
// w1 现在为 nullptr
// 不能复制
// std::unique_ptr<Widget> w4 = w3; // 编译错误
}
// 返回unique_ptr的工厂函数
std::unique_ptr<Widget> create_widget(int id) {
return std::make_unique<Widget>(id);
}
// unique_ptr的容器
void container_example() {
std::vector<std::unique_ptr<Widget>> widgets;
widgets.push_back(std::make_unique<Widget>(1));
widgets.push_back(std::make_unique<Widget>(2));
// 从容器中移动
auto w = std::move(widgets[0]);
// widgets[0] 现在为 nullptr
}
// 自定义删除器
struct FileCloser {
void operator()(std::FILE* fp) const {
if (fp) {
std::cout << "关闭文件
";
std::fclose(fp);
}
}
};
void custom_deleter_example() {
std::unique_ptr<std::FILE, FileCloser> file(
std::fopen("data.txt", "r")
);
// Lambda删除器
auto deleter = [](int* p) {
std::cout << "删除: " << *p << "
";
delete p;
};
std::unique_ptr<int, decltype(deleter)> ptr(new int(42), deleter);
}
shared_ptr - 共享所有权
shared_ptr通过引用计数实现共享所有权,允许多个指针指向同一对象。
#include <memory>
#include <vector>
#include <iostream>
class Resource {
int id_;
public:
Resource(int id) : id_(id) {
std::cout << "Resource " << id_ << " 创建
";
}
~Resource() {
std::cout << "Resource " << id_ << " 销毁
";
}
int id() const { return id_; }
};
void shared_ptr_basics() {
// 创建shared_ptr
std::shared_ptr<Resource> r1(new Resource(1));
auto r2 = std::make_shared<Resource>(2); // 推荐 - 单次分配
// 共享所有权
std::shared_ptr<Resource> r3 = r2;
std::cout << "使用计数: " << r2.use_count() << "
"; // 2
// 多个所有者
{
std::shared_ptr<Resource> r4 = r2;
std::cout << "使用计数: " << r2.use_count() << "
"; // 3
} // r4 销毁
std::cout << "使用计数: " << r2.use_count() << "
"; // 2
// 检查是否有效
if (r2) {
std::cout << "r2 有效
";
}
// 重置
r2.reset(); // 减少引用计数
std::cout << "使用计数: " << r3.use_count() << "
"; // 1
}
// 数据结构中的共享所有权
class Node {
public:
int value;
std::shared_ptr<Node> next;
Node(int v) : value(v), next(nullptr) {
std::cout << "Node " << value << " 创建
";
}
~Node() {
std::cout << "Node " << value << " 销毁
";
}
};
void linked_list_example() {
auto head = std::make_shared<Node>(1);
head->next = std::make_shared<Node>(2);
head->next->next = std::make_shared<Node>(3);
// 当head超出作用域时,所有节点自动销毁
}
// 在shared_ptr和unique_ptr之间转换
void pointer_conversion() {
// unique_ptr 到 shared_ptr
auto u = std::make_unique<Resource>(1);
std::shared_ptr<Resource> s = std::move(u);
// u 现在为 nullptr
// 不能将shared_ptr转换为unique_ptr(共享所有权)
}
// 别名构造函数
struct Data {
int x, y;
};
void aliasing_example() {
auto data = std::make_shared<Data>();
data->x = 10;
data->y = 20;
// 创建指向成员的shared_ptr,但保持整个对象存活
std::shared_ptr<int> px(data, &data->x);
std::cout << "使用计数: " << data.use_count() << "
"; // 2
}
weak_ptr - 打破循环引用
weak_ptr提供对shared_ptr对象的非拥有引用,防止循环引用导致的内存泄漏。
#include <memory>
#include <iostream>
// 没有weak_ptr:循环引用泄漏
class BadParent;
class BadChild {
public:
std::shared_ptr<BadParent> parent;
~BadChild() { std::cout << "子对象销毁
"; }
};
class BadParent {
public:
std::shared_ptr<BadChild> child;
~BadParent() { std::cout << "父对象销毁
"; }
};
void circular_reference_leak() {
auto parent = std::make_shared<BadParent>();
auto child = std::make_shared<BadChild>();
parent->child = child;
child->parent = parent; // 循环引用 - 内存泄漏!
} // 两个对象都未销毁!
// 使用weak_ptr:打破循环
class Parent;
class Child {
public:
std::weak_ptr<Parent> parent; // weak_ptr打破循环
~Child() { std::cout << "子对象销毁
"; }
};
class Parent {
public:
std::shared_ptr<Child> child;
~Parent() { std::cout << "父对象销毁
"; }
};
void weak_ptr_example() {
auto parent = std::make_shared<Parent>();
auto child = std::make_shared<Child>();
parent->child = child;
child->parent = parent; // 无循环引用
} // 两个对象都正确销毁
// 使用weak_ptr
void weak_ptr_usage() {
std::weak_ptr<Resource> weak;
{
auto shared = std::make_shared<Resource>(1);
weak = shared;
std::cout << "使用计数: " << shared.use_count() << "
"; // 1
std::cout << "弱引用计数: " << weak.use_count() << "
"; // 1
// 锁定以访问对象
if (auto locked = weak.lock()) {
std::cout << "资源仍存活: " << locked->id()
<< "
";
std::cout << "使用计数: " << locked.use_count() << "
"; // 2
}
} // shared 销毁
// 对象已消失
if (auto locked = weak.lock()) {
std::cout << "资源仍存活
";
} else {
std::cout << "资源已销毁
";
}
std::cout << "已过期: " << weak.expired() << "
"; // true
}
// 使用weak_ptr的观察者模式
class Observable {
std::vector<std::weak_ptr<class Observer>> observers_;
public:
void attach(std::shared_ptr<Observer> observer) {
observers_.push_back(observer);
}
void notify() {
// 清理已过期的观察者
observers_.erase(
std::remove_if(observers_.begin(), observers_.end(),
[](const auto& weak) { return weak.expired(); }),
observers_.end()
);
// 通知活跃观察者
for (auto& weak : observers_) {
if (auto observer = weak.lock()) {
// 通知观察者
}
}
}
};
enable_shared_from_this
enable_shared_from_this允许对象安全地创建指向自身的shared_ptr实例。
#include <memory>
#include <iostream>
#include <vector>
class Task : public std::enable_shared_from_this<Task> {
int id_;
std::vector<std::shared_ptr<Task>> dependencies_;
public:
Task(int id) : id_(id) {}
void add_dependency(std::shared_ptr<Task> dep) {
dependencies_.push_back(dep);
}
// 将此任务注册为另一个的依赖
void register_with(std::shared_ptr<Task> other) {
// 获取指向此对象的shared_ptr
other->add_dependency(shared_from_this());
}
int id() const { return id_; }
};
void shared_from_this_example() {
auto task1 = std::make_shared<Task>(1);
auto task2 = std::make_shared<Task>(2);
task1->register_with(task2);
// task2 现在有一个指向task1的shared_ptr
}
// 回调注册
class Button : public std::enable_shared_from_this<Button> {
using Callback = std::function<void(std::shared_ptr<Button>)>;
Callback on_click_;
public:
void set_on_click(Callback callback) {
on_click_ = callback;
}
void click() {
if (on_click_) {
on_click_(shared_from_this());
}
}
};
void callback_example() {
auto button = std::make_shared<Button>();
button->set_on_click([](std::shared_ptr<Button> btn) {
std::cout << "按钮点击!
";
});
button->click();
}
自定义删除器
自定义删除器使智能指针能够管理非内存资源,如文件句柄、套接字和数据库连接。
#include <memory>
#include <iostream>
#include <cstdio>
// 函数删除器
void close_file(std::FILE* fp) {
if (fp) {
std::cout << "关闭文件
";
std::fclose(fp);
}
}
// shared_ptr的Lambda删除器
void shared_ptr_deleter() {
std::shared_ptr<std::FILE> file(
std::fopen("data.txt", "r"),
[](std::FILE* fp) {
if (fp) {
std::cout << "Lambda 关闭文件
";
std::fclose(fp);
}
}
);
}
// unique_ptr的数组删除器
void array_deleter() {
// 内置数组支持
std::unique_ptr<int[]> arr(new int[10]);
arr[0] = 42; // 自动数组删除[]
// 自定义数组删除器
std::unique_ptr<int[], void(*)(int*)> custom_arr(
new int[10],
[](int* p) {
std::cout << "自定义数组删除
";
delete[] p;
}
);
}
// 带自定义删除器的资源包装器
template<typename T>
class ResourcePool {
std::vector<std::unique_ptr<T>> pool_;
public:
std::shared_ptr<T> acquire() {
if (pool_.empty()) {
return std::shared_ptr<T>(
new T(),
[this](T* ptr) { this->release(ptr); }
);
}
auto ptr = pool_.back().release();
pool_.pop_back();
return std::shared_ptr<T>(
ptr,
[this](T* p) { this->release(p); }
);
}
private:
void release(T* ptr) {
pool_.push_back(std::unique_ptr<T>(ptr));
}
};
性能考虑
智能指针有不同的性能特性,影响设计决策。
#include <memory>
#include <chrono>
#include <iostream>
struct Data {
int values[100];
};
void performance_comparison() {
using namespace std::chrono;
// unique_ptr:零开销
{
auto start = high_resolution_clock::now();
for (int i = 0; i < 1000000; ++i) {
auto ptr = std::make_unique<Data>();
}
auto end = high_resolution_clock::now();
std::cout << "unique_ptr: "
<< duration_cast<milliseconds>(end - start).count()
<< "ms
";
}
// shared_ptr:引用计数开销
{
auto start = high_resolution_clock::now();
for (int i = 0; i < 1000000; ++i) {
auto ptr = std::make_shared<Data>();
}
auto end = high_resolution_clock::now();
std::cout << "shared_ptr: "
<< duration_cast<milliseconds>(end - start).count()
<< "ms
";
}
// shared_ptr复制:原子操作
{
auto ptr = std::make_shared<Data>();
auto start = high_resolution_clock::now();
for (int i = 0; i < 1000000; ++i) {
auto copy = ptr; // 原子递增/递减
}
auto end = high_resolution_clock::now();
std::cout << "shared_ptr 复制: "
<< duration_cast<milliseconds>(end - start).count()
<< "ms
";
}
}
// make_shared 与构造函数
void allocation_efficiency() {
// 一次分配(对象 + 控制块)
auto s1 = std::make_shared<Data>();
// 两次分配(对象,然后控制块)
std::shared_ptr<Data> s2(new Data());
// 推荐使用make_shared以提高效率和异常安全
}
线程安全
智能指针提供特定的线程安全保证,需理解以进行并发编程。
#include <memory>
#include <thread>
#include <vector>
#include <iostream>
void thread_safety() {
auto shared = std::make_shared<int>(42);
// 引用计数是线程安全的
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([shared]() {
auto copy = shared; // 安全:原子递增
std::cout << *copy << "
";
});
}
for (auto& t : threads) {
t.join();
}
// 修改指向的对象不是线程安全的
auto data = std::make_shared<int>(0);
std::vector<std::thread> unsafe_threads;
for (int i = 0; i < 10; ++i) {
unsafe_threads.emplace_back([data]() {
++(*data); // 竞态条件!
});
}
for (auto& t : unsafe_threads) {
t.join();
}
}
// 原子shared_ptr操作(C++20)
void atomic_shared_ptr() {
std::shared_ptr<int> shared = std::make_shared<int>(42);
// 线程 1
std::thread t1([&shared]() {
auto local = std::atomic_load(&shared);
});
// 线程 2
std::thread t2([&shared]() {
auto new_value = std::make_shared<int>(100);
std::atomic_store(&shared, new_value);
});
t1.join();
t2.join();
}
最佳实践
- 优先使用
make_unique和make_shared而非显式new,以提高异常安全和效率 - 默认使用
unique_ptr;仅在真正需要共享所有权时使用shared_ptr - 使用
weak_ptr在父子关系中打破循环引用 - 切勿在从
get()获取的原始指针上调用delete - 传递
unique_ptr按值以转移所有权,按引用以使用而不转移 - 传递
shared_ptr按常量引用以观察,按值以共享所有权 - 使用自定义删除器管理非内存资源
- 避免多次从同一原始指针创建
shared_ptr(创建多个控制块) - 在实现自定义智能指针类时,将移动操作标记为
noexcept - 当对象需要创建指向自身的
shared_ptr时,使用enable_shared_from_this
常见陷阱
- 从同一原始指针创建多个
shared_ptr实例,导致双重释放 - 当
unique_ptr足够时,在容器中存储shared_ptr,浪费内存 - 忘记使用
weak_ptr打破循环引用,导致内存泄漏 - 在对象由
shared_ptr管理之前调用shared_from_this() - 不必要地按值传递智能指针,复制引用计数
- 使用
reset()而非赋值,可能过早销毁对象 - 假设指向对象的线程安全(仅控制块线程安全)
- 在使用返回的
shared_ptr前未检查weak_ptr::lock()是否成功 - 对数组使用
unique_ptr<T>而非unique_ptr<T[]>语法 - 在同一代码库中混合手动内存管理和智能指针
何时使用智能指针
在以下情况下使用智能指针:
- 无需垃圾回收的自动内存管理
- 在API中清晰表达所有权语义
- 遵循RAII的异常安全资源管理
- 防止复杂控制流中的内存泄漏
- 跨多个组件共享对象所有权
- 使用弱指针打破循环引用
- 用自定义删除器管理非内存资源
- 避免手动
new和delete的现代C++代码 - 并发访问的线程安全引用计数
- 与标准库容器和算法的互操作性