以下是关于 C++ 智能指针的系统整理,包含核心概念、经典示例及优势总结。
一、智能指针的核心概念
C++ 智能指针是一种自动管理动态内存的工具,通过 RAII(资源获取即初始化)机制,在对象生命周期结束时自动释放内存,避免手动管理内存导致的内存泄漏和悬垂指针问题。
二、智能指针的三种类型
C++ 标准库提供了三种智能指针(需包含头文件 <memory>
):
1. unique_ptr
(独占所有权指针)
核心特性:
- 独占资源所有权:同一时间仅允许一个
unique_ptr
指向该资源。 - 不可拷贝(禁用拷贝构造函数和赋值运算符),但可通过
std::move
转移所有权。 - 生命周期结束时(如离开作用域)自动释放资源。
经典示例:
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass(int id) : id_(id) {
std::cout << "MyClass " << id_ << " 构造" << std::endl;
}
~MyClass() {
std::cout << "MyClass " << id_ << " 析构" << std::endl;
}
private:
int id_;
};
int main() {
// 创建 unique_ptr,指向 MyClass 对象(id=1)
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>(1);
// 转移所有权(ptr2 接管资源,ptr1 变为空)
std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
// ptr1 已无资源,不可访问(否则崩溃)
// if (ptr1) { ... } // 安全检查:ptr1 为空时条件不成立
// ptr2 离开作用域时自动释放资源
return 0;
}
输出结果:
MyClass 1 构造
MyClass 1 析构
2. shared_ptr
(共享所有权指针)
核心特性:
- 共享资源所有权:多个
shared_ptr
可通过引用计数(use_count()
)共享同一资源。 - 引用计数为 0 时(所有
shared_ptr
销毁或离开作用域)自动释放资源。 - 支持拷贝和赋值(引用计数递增)。
经典示例:
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass(int id) : id_(id) {
std::cout << "MyClass " << id_ << " 构造" << std::endl;
}
~MyClass() {
std::cout << "MyClass " << id_ << " 析构" << std::endl;
}
};
int main() {
// 创建 shared_ptr,指向 MyClass 对象(id=2)
std::shared_ptr<MyClass> ptrA = std::make_shared<MyClass>(2);
std::cout << "当前引用计数:" << ptrA.use_count() << std::endl; // 输出:1
// 拷贝 ptrA(引用计数递增)
std::shared_ptr<MyClass> ptrB = ptrA;
std::cout << "当前引用计数:" << ptrA.use_count() << std::endl; // 输出:2
// ptrB 离开作用域(引用计数减 1)
{
std::shared_ptr<MyClass> ptrC = ptrA;
std::cout << "当前引用计数:" << ptrA.use_count() << std::endl; // 输出:3
} // ptrC 销毁,引用计数回到 2
// ptrA 和 ptrB 离开作用域(引用计数减为 0,资源释放)
return 0;
}
输出结果:
MyClass 2 构造
当前引用计数:1
当前引用计数:2
当前引用计数:3
MyClass 2 析构
3. weak_ptr
(弱引用指针)
核心特性:
- 配合
shared_ptr
使用,解决循环引用问题(shared_ptr
相互引用导致引用计数无法归零)。 - 不参与引用计数(
use_count()
仅反映关联的shared_ptr
数量)。 - 需通过
lock()
方法获取shared_ptr
才能访问资源(若资源已释放则返回空)。
经典示例(解决循环引用):
#include <iostream>
#include <memory>
class B; // 前置声明
class A {
public:
std::weak_ptr<B> b_ptr; // 用 weak_ptr 避免循环引用
~A() { std::cout << "A 析构" << std::endl; }
};
class B {
public:
std::weak_ptr<A> a_ptr; // 用 weak_ptr 避免循环引用
~B() { std::cout << "B 析构" << std::endl; }
};
int main() {
auto a = std::make_shared<A>(); // a 的引用计数=1
auto b = std::make_shared<B>(); // b 的引用计数=1
a->b_ptr = b; // weak_ptr 不增加 b 的引用计数(b.use_count() 仍为 1)
b->a_ptr = a; // weak_ptr 不增加 a 的引用计数(a.use_count() 仍为 1)
return 0; // a 和 b 离开作用域,引用计数归零,正常析构
}
输出结果:
B 析构
A 析构
对比:若用 shared_ptr
会导致循环引用(无法析构,内存泄漏):
// 错误示例(循环引用)
class A { public: std::shared_ptr<B> b_ptr; ~A() { ... } };
class B { public: std::shared_ptr<A> a_ptr; ~B() { ... } };
// main 中创建 a 和 b 后,a->b_ptr = b(b.use_count=2),b->a_ptr = a(a.use_count=2)
// 离开作用域时,a 和 b 的引用计数减为 1(相互引用),无法归零,资源无法释放!
三、智能指针的核心优势
优势 | 说明 |
---|---|
自动内存管理 | 生命周期结束时自动释放资源,无需手动 delete ,避免内存泄漏。 |
异常安全 | 即使发生异常,智能指针仍会在作用域结束时释放资源(RAII 保证)。 |
避免悬垂指针 | 资源释放后,所有指向它的智能指针自动失效(如 unique_ptr 置空,shared_ptr 引用计数归零)。 |
明确所有权语义 | unique_ptr 强制独占,shared_ptr 明确共享,weak_ptr 避免循环依赖,代码意图更清晰。 |
四、使用场景总结
unique_ptr
:单线程中独占资源(如函数内部管理临时对象)。shared_ptr
:多线程或多对象共享资源(如容器中的对象、需要被多个模块访问的资源)。weak_ptr
:解决shared_ptr
的循环引用(如双向链表、对象间相互引用)。
通过智能指针,C++ 开发者可以更安全、高效地管理动态内存,大幅减少手动内存管理的错误。实际编码中,优先使用 unique_ptr
(轻量、无额外开销),仅在需要共享时使用 shared_ptr
,并通过 weak_ptr
避免循环引用。