智能指针详解
·
智能指针是 C++ 标准库(<memory> 头文件)提供的内存管理工具,核心目标是替代裸指针(T*),自动管理堆内存的生命周期,避免手动 new/delete 导致的内存泄漏、重复释放、野指针等问题。其底层原理是RAII(资源获取即初始化):将堆内存的释放逻辑绑定到智能指针的析构函数,当智能指针出作用域/被销毁时,自动调用 delete 释放指向的内存。
一、智能指针的核心概念
1. 所有权模型
智能指针的核心是对“堆对象所有权”的管理,主要分为两类:
- 独占所有权:一个对象只能被一个智能指针管理(如
unique_ptr); - 共享所有权:多个智能指针可以共享一个对象,最后一个指针销毁时才释放对象(如
shared_ptr); - 弱引用:不拥有对象所有权,仅观察共享对象(如
weak_ptr)。
2. 核心优势
- 自动释放内存:无需手动调用
delete,避免遗忘释放导致的内存泄漏; - 异常安全:即使代码中途抛出异常,智能指针析构函数仍会执行,保证内存释放;
- 避免重复释放:通过所有权规则,杜绝多次
delete同一内存; - 野指针防护:智能指针销毁后自动置空(部分场景),减少野指针风险。
二、C++ 智能指针的全量用法
C++ 标准库提供 3 种核心智能指针(C++11 及以上),以及 C++14 新增的辅助创建函数,以下是完整用法:
1. std::unique_ptr(独占式智能指针)
核心特性
- 独占对象所有权,不可拷贝,仅可移动(
std::move); - 轻量级(仅封装一个裸指针),无额外性能开销;
- 支持管理数组(
unique_ptr<T[]>)。
常用操作
| 操作 | 说明 |
|---|---|
std::make_unique<T>(args...) |
C++14 新增:创建 unique_ptr,自动调用 T 的构造函数(推荐); |
unique_ptr<T> ptr(new T()) |
手动创建(不推荐,异常安全差); |
ptr->func() / (*ptr).func() |
访问对象成员(同裸指针); |
ptr.reset() |
释放对象,指针置空; |
ptr.reset(new T()) |
释放旧对象,指向新对象(不推荐,优先用 make_unique); |
ptr.release() |
释放所有权,返回裸指针(需手动管理内存); |
ptr.get() |
获取内部裸指针(仅观察,不释放所有权); |
std::move(ptr) |
转移所有权,原指针变为空; |
unique_ptr<T[]> arr_ptr |
管理数组(自动调用 delete[],而非 delete); |
示例代码
#include <memory>
#include <iostream>
class Computer {
public:
Computer(const std::string& brand) : brand_(brand) {
std::cout << brand_ << " 电脑创建" << std::endl;
}
~Computer() {
std::cout << brand_ << " 电脑销毁" << std::endl;
}
void powerOn() {
std::cout << brand_ << " 开机" << std::endl;
}
private:
std::string brand_;
};
// unique_ptr 基础用法
void unique_ptr_demo() {
// 1. 创建:优先用 make_unique(C++14)
std::unique_ptr<Computer> comp1 = std::make_unique<Computer>("联想");
// 2. 访问成员
comp1->powerOn();
// 3. 移动所有权(不可拷贝)
std::unique_ptr<Computer> comp2 = std::move(comp1); // comp1 变为空
if (!comp1) {
std::cout << "comp1 已失去所有权" << std::endl;
}
comp2->powerOn();
// 4. 管理数组
std::unique_ptr<Computer[]> comp_arr = std::make_unique<Computer[]>(2);
comp_arr[0] = Computer("戴尔");
comp_arr[1] = Computer("惠普");
// 5. 释放所有权(需手动delete)
Computer* raw_ptr = comp2.release();
delete raw_ptr; // 必须手动释放,否则内存泄漏
}
2. std::shared_ptr(共享式智能指针)
核心特性
- 共享对象所有权,可拷贝、可赋值;
- 内部维护引用计数:每拷贝一次,计数+1;每销毁一个指针,计数-1;计数为 0 时释放对象;
- 支持自定义删除器(如管理非
new创建的资源); - 注意:避免循环引用(否则计数无法归 0,导致内存泄漏)。
常用操作
| 操作 | 说明 |
|---|---|
std::make_shared<T>(args...) |
创建 shared_ptr,内存更高效(一次分配对象+引用计数); |
shared_ptr<T> ptr(new T()) |
手动创建(不推荐,两次内存分配); |
ptr.use_count() |
获取当前引用计数; |
ptr.unique() |
判断是否独占对象(计数为 1); |
ptr.reset() |
释放当前指针的所有权,计数-1; |
ptr.get() |
获取内部裸指针; |
std::static_pointer_cast |
智能指针版的 static_cast; |
std::dynamic_pointer_cast |
智能指针版的 dynamic_cast; |
示例代码
void shared_ptr_demo() {
// 1. 创建
std::shared_ptr<Computer> comp1 = std::make_shared<Computer>("苹果");
std::cout << "引用计数:" << comp1.use_count() << std::endl; // 输出 1
// 2. 拷贝:计数+1
std::shared_ptr<Computer> comp2 = comp1;
std::cout << "引用计数:" << comp1.use_count() << std::endl; // 输出 2
// 3. 重置:计数-1
comp1.reset();
std::cout << "引用计数:" << comp2.use_count() << std::endl; // 输出 1
// 4. 自定义删除器(比如管理 FILE* 资源)
auto file_deleter = [](FILE* fp) {
fclose(fp);
std::cout << "文件已关闭" << std::endl;
};
std::shared_ptr<FILE> fp_ptr(fopen("test.txt", "w"), file_deleter);
}
3. std::weak_ptr(弱引用智能指针)
核心特性
- 不拥有对象所有权,仅观察
shared_ptr管理的对象; - 不影响引用计数,避免循环引用;
- 需转换为
shared_ptr才能访问对象(通过lock()方法)。
常用操作
| 操作 | 说明 |
|---|---|
weak_ptr<T> wp = sp |
从 shared_ptr 构造弱引用; |
wp.lock() |
转换为 shared_ptr:对象存在则返回有效指针,否则返回空; |
wp.expired() |
判断观察的对象是否已被释放; |
wp.use_count() |
获取观察对象的引用计数; |
示例代码(解决循环引用)
// 循环引用场景:两个对象互相持有 shared_ptr,导致计数无法归 0
class A;
class B;
class A {
public:
std::weak_ptr<B> b_ptr; // 用 weak_ptr 替代 shared_ptr
~A() { std::cout << "A 销毁" << std::endl; }
};
class B {
public:
std::weak_ptr<A> a_ptr; // 用 weak_ptr 替代 shared_ptr
~B() { std::cout << "B 销毁" << std::endl; }
};
void weak_ptr_demo() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
// 互相引用:weak_ptr 不增加计数
a->b_ptr = b;
b->a_ptr = a;
// 访问弱引用对象:需 lock() 转换为 shared_ptr
if (auto b_sp = a->b_ptr.lock()) {
std::cout << "B 对象有效" << std::endl;
}
}
4. 废弃的智能指针(不推荐使用)
std::auto_ptr:C++98 引入,已被unique_ptr替代(拷贝语义有缺陷,会转移所有权,易出错);- 注意:不要使用
auto_ptr,优先用unique_ptr。
三、综合示例:智能指针实战场景
以下示例模拟“电脑设备管理系统”,综合使用 unique_ptr(独占设备)、shared_ptr(共享配置)、weak_ptr(观察状态),覆盖核心用法:
#include <memory>
#include <iostream>
#include <vector>
#include <string>
// 配置类:多台电脑共享同一配置
class Config {
public:
Config(int memory, std::string os) : memory_(memory), os_(os) {
std::cout << "配置创建:内存 " << memory_ << "G,系统 " << os_ << std::endl;
}
~Config() {
std::cout << "配置销毁" << std::endl;
}
void show() const {
std::cout << "当前配置:" << memory_ << "G | " << os_ << std::endl;
}
private:
int memory_;
std::string os_;
};
// 电脑类:独占自身硬件,共享配置
class Computer {
public:
Computer(std::string brand, std::shared_ptr<Config> cfg)
: brand_(brand), cfg_(cfg), status_(std::make_unique<bool>(false)) {}
~Computer() {
std::cout << brand_ << " 销毁" << std::endl;
}
void powerOn() {
*status_ = true;
std::cout << brand_ << " 开机:";
cfg_->show();
}
// 提供弱引用观察状态(不拥有所有权)
std::weak_ptr<bool> getStatus() const {
return std::shared_ptr<bool>(status_); // 转换为 shared_ptr 后构造 weak_ptr
}
private:
std::string brand_;
std::shared_ptr<Config> cfg_; // 共享配置
std::unique_ptr<bool> status_; // 独占状态(开机/关机)
};
int main() {
// 1. 创建共享配置(多台电脑共用)
std::shared_ptr<Config> global_cfg = std::make_shared<Config>(16, "Windows 11");
// 2. 创建多台电脑(独占自身,共享配置)
std::vector<std::unique_ptr<Computer>> computers;
computers.emplace_back(std::make_unique<Computer>("联想", global_cfg));
computers.emplace_back(std::make_unique<Computer>("戴尔", global_cfg));
computers.emplace_back(std::make_unique<Computer>("惠普", global_cfg));
// 3. 开机并观察状态
for (auto& comp : computers) {
comp->powerOn();
// 弱引用观察状态
std::weak_ptr<bool> status_wp = comp->getStatus();
if (auto status_sp = status_wp.lock()) {
std::cout << "状态:" << (*status_sp ? "开机" : "关机") << std::endl;
}
}
// 4. 查看配置引用计数
std::cout << "配置引用计数:" << global_cfg.use_count() << std::endl; // 输出 4(3台电脑 + global_cfg)
// 5. 销毁部分电脑
computers.erase(computers.begin() + 1); // 删除戴尔电脑
std::cout << "删除戴尔后,配置计数:" << global_cfg.use_count() << std::endl; // 输出 3
return 0;
}
输出结果
配置创建:内存 16G,系统 Windows 11
联想 开机:当前配置:16G | Windows 11
状态:开机
戴尔 开机:当前配置:16G | Windows 11
状态:开机
惠普 开机:当前配置:16G | Windows 11
状态:开机
配置引用计数:4
戴尔 销毁
删除戴尔后,配置计数:3
惠普 销毁
联想 销毁
配置销毁
示例说明
shared_ptr<Config>:3 台电脑共享同一配置,只有最后一个指针销毁时,配置才会释放;unique_ptr<Computer>:vector 中存储独占式指针,删除元素时自动释放对应电脑对象;weak_ptr<bool>:观察电脑状态,不影响状态对象的生命周期,避免循环引用;- 全程无手动
delete,所有内存由智能指针自动释放,无内存泄漏。
四、关键注意事项
- 头文件:所有智能指针都需要包含
<memory>头文件; - 版本兼容:
make_unique是 C++14 特性,C++11 需手动写unique_ptr<T>(new T()); - 避免裸指针混用:不要将智能指针的裸指针(
get()返回)再封装为智能指针,否则会重复释放; - 循环引用:
shared_ptr互相引用时,必须用weak_ptr打破循环; - 数组管理:
unique_ptr<T[]>专门管理数组,自动调用delete[],而shared_ptr需自定义删除器; - 性能:
unique_ptr无额外开销,shared_ptr因引用计数有轻微开销(原子操作)。
总结
- 智能指针的核心是RAII 机制,通过析构函数自动释放内存,替代裸指针的手动管理;
unique_ptr适用于独占所有权场景(轻量、高效),shared_ptr适用于共享所有权场景(需注意循环引用),weak_ptr用于观察共享对象;- 优先使用
make_unique/make_shared创建智能指针,避免手动new,提升异常安全性; - 智能指针是 C++ 内存管理的最佳实践,彻底解决裸指针的内存泄漏、重复释放等问题。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)