【深入解析C++智能指针原理与应用】
目录
一、定义
C++11智能指针是封装了普通指针的类模板,核心作用是自动管理内存——利用RALL(资源获取即初始化)机制,在智能指针对象生命周期结束时(出作用域、析构),自动释放指向的堆内存,从根本上避免内存泄漏和野指针问题。
二、自定义智能指针
为了更好的理解智能指针的原理,我们来是手写一个智能指针
//定义结构体person
struct Person {
int pid;
string name;
int age;
public:
Person(int pid,int age,string name):pid(pid),name(name),age(age){}
void show() {
cout << pid << " | " << name << " | " << age << endl;
}
~Person() {
cout << "~Person():" << this << endl;
}
};
//智能指针类
class AutoPersonPtr {
Person* p;//指向堆区new person地址
public:
AutoPersonPtr(Person*p):p(p){}//1.接收外部已经new好的指针
AutoPersonPtr(int pid, int age, string name) {//2.传入参数,构造函数内部帮你new
p = new Person(pid, age, name);
}
AutoPersonPtr(const AutoPersonPtr& o) = delete;//禁用拷贝构造
void operator=(const AutoPersonPtr& o) = delete;//禁用拷贝赋值重载
AutoPersonPtr(AutoPersonPtr&& o) = delete;//禁用移动构造
void operator=(AutoPersonPtr&& o) = delete;//禁用移动赋值
Person* operator->() {
return p;
}
Person& operator*() {
return *p;
}
void reset() {//手动释放内存
if (p != nullptr) {
delete p;
p = nullptr;
}
}
~AutoPersonPtr() {
if (p != nullptr) {
delete p;//自动释放指针
}
}
};
int main() {
AutoPersonPtr p1(new Person(1, 12, "jack"));
AutoPersonPtr p2(2, 18, "jenny");
p1->show();
p2->show();
(*p1).show();
//手动释放
//p1.reset();
//p2.reset();
cout << "-----main over------" << endl;
return 0;
}
void reset() {//手动释放内存
if (p != nullptr) {
delete p;
p = nullptr;
}
}
1.自定义成员工具函数:void reset()
- 功能:手动提前释放托管堆内存,不用等到智能指针析构;
- 逻辑:判空
p!=nullptr→delete 释放堆对象→指针置空p=nullptr,避免野指针; - 特点:析构会二次判空,已经 reset 置空的指针,析构不会重复释放;
- 调用:
p1.reset();。
p1->show();
p2->show();
(*p1).show();
2.*/->关键区分
->:运算符重载返回指针,指针用 -> 调函数*:运算符重载返回对象引用,实体对象用。调函数
3.进阶:将自定义的智能指针提升为类模板
template<class T>
class UniPtr {
T* ptr;
public:
UniPtr(T* ptr) : ptr(ptr) {}
~UniPtr() {
if (ptr != nullptr) delete ptr;
}
T* operator->() { return ptr; }
T& operator*() { return *ptr; }
void reset() {
if (ptr != nullptr) {
delete ptr;
ptr = nullptr;
}
}
T& get() {
return *ptr;
}
};
三、四种类型的智能指针
前提:定义在<memory>头文件中

先说一下auto_ptr的缺陷
-
auto_ptr拷贝构造 / 赋值时,资源所有权直接夺走,原对象变成空指针(核心缺陷)
-
不支持数组:auto_ptr<T[]>非法,不能管理 new [] 开辟的数组内存,析构只用delete不用delete[],数组内存泄漏。
-
缺少const限制:const auto_ptr<T>只能锁住指针变量不能改指向,但可以通过 get () 修改堆里数据,设计不严谨。
-
赋值重载隐患:自赋值会释放自身资源,产生未定义行为。
等等问题...
1.unique_ptr
1.1 语法
方式1:new初始化
unique_ptr<Person> p1(new Person(1,20,"张三"));
方式2:C++14推荐 make_unique(优先使用,安全)
auto p2 = make_unique<Person>(2,22,"李四");
不允许拷贝,只允许移动所有权!
unique_ptr<Person> p3 = p1; //特点是独占所有权
auto p3 = move(p1); //p1变空,资源转给p3
1.2 常用成员函数
| 函数 | 作用 |
|---|---|
.get() |
返回内部裸指针 Person* |
.reset(裸指针) |
释放原有资源,接管新指针;无参直接释放、置空 |
.release() |
交出资源所有权,返回裸指针,unique_ptr 自身置空(不会 delete) |
.swap(up) |
交换两个 unique_ptr 托管资源 |
.operator->() |
p->show()重载箭头 |
.operator*() |
(*p).show()解引用 |
.bool() |
if(p):指针非空 true,空则 false |
1.3 核心特点
- 独占所有权:同一时间,只有一个unique_ptr能指向某块堆内存,不允许拷贝,只支持移动
- 轻量级:无额外开销(无引用计数),效率接近普通指针
- 自动释放:析构时释放指向的内存,也可以手动调reset()释放
#include<memory>
int main() {
unique_ptr<int>p1(new int(88));
cout << p1.get() << endl;
//p1.reset();//直接回收
int *src_p1=p1.release();//返回原指针,需要自己回收
delete src_p1;
unique_ptr<Person>p2(new Person(1008, 21, "jyyy"));
cout << p2.get() << endl;
p2->show();
(*p2).show();
Person* person = new Person(1009, 22, "jyyy");
unique_ptr<Person>up(person);
up->show();
//因为unique_ptr是独占式
//同一个指针不能放在两个智能指针对象中
//以下代码则报异常
unique_ptr<Person>up2(person);
up2->show();
// 可以移动
unique_ptr<Person> up3 = move(up);
up3->show();
return 0;
}
1.4 适用场景
- 绝大多数单个对象资源管理(日常开发首选),函数内临时堆对象、类成员资源;
-
auto p = make_unique<Person>(1,18,"张三"); - 容器存放堆对象:
vector<unique_ptr<Person>>,容器销毁自动释放全部元素,不会内存泄漏; - 作为函数返回值:函数返回
unique_ptr,编译器优化隐式移动,安全转交资源; - 禁止多指针共用同一块内存,资源只允许一个管理者。
不适合
需要多个指针同时共用同一个堆对象。
2.shared_ptr
2.1 定义语法
//写法1
shared_ptr<Person> s1(new Person(1,18,"A"));
//写法2 推荐 make_shared(效率高)
auto s2 = make_shared<Person>(2,19,"B");
//允许拷贝,计数+1
shared_ptr<Person> s3 = s2; //s2、s3共用同一块内存,引用计数=2
原理:堆上额外存引用计数器,每多一个 shared_ptr 指向,count++;析构 count--,count==0 释放堆对象。
2.2 常用成员函数
| 函数 | 功能 |
|---|---|
.use_count() |
返回当前引用计数值(int) |
.unique() |
返回 bool:count==1为 true,否则 false |
.get() |
获取原生裸指针 |
.reset() |
无参:计数 - 1、清空当前指针;带参替换托管对象 |
.swap(sp) |
交换资源 |
->、* |
正常重载,访问成员 |
2.3 核心特性
- 共享所有权:多个shared_ptr指针可指向同一块内存
- 引用计数
- 线程安全
int main() {
shared_ptr<Person> p1(new Person(1008, "Jack", 21));
if (p1.unique()) {
cout << "p1 即是 unique_ptr" << endl;
}
p1->show();
{
// 多个共享智能指针,同时指向同一个堆内容(数据)
shared_ptr<Person> p2 = p1;
shared_ptr<Person> p3 = p1;
shared_ptr<Person> p4 = p1;
p2->show();
p3->show();
p4->show();
cout << "inblock 引用计数: " << p1.use_count() << endl;
}
cout << "outblock 引用计数: " << p1.use_count() << endl;
p1.reset();
cout << "over 引用计数: " << p1.use_count() << endl;
return 0;
}
2.4 适用场景
- 多个对象共享同一份资源:多个类成员、多个函数同时持有同一个堆资源;
- 跨函数、跨线程共享资源:多线程访问同一个对象,用 shared_ptr 保证资源不会提前释放;
- 不确定资源什么时候销毁,多方共用;
不适合
简单独占场景(多余性能损耗)。
3.weak_ptr
不能单独管理内存,只能从 shared_ptr 构造
3.1 定义语法
auto sp = make_shared<Person>(1,20,"小明");
weak_ptr<Person> wp = sp; //绑定shared_ptr,不增加计数
weak_ptr不能直接 ->、*访问对象,必须先升级成 shared_ptr。
3.2 常用成员函数
| 函数 | 作用 |
|---|---|
.lock() |
返回shared_ptr<T>,升级为共享指针;资源已释放返回空 shared |
.expired() |
bool:资源已销毁返回 true,可用 false |
.reset() |
清空 weak_ptr,不再监视资源 |
.swap(wk) |
交换两个 weak_ptr |
.use_count() |
查看绑定的 shared_ptr 当前引用计数 |
3.3 核心特性
- 弱引用:指向shared_ptr管理的内存,但不增加引用计数
- 不拥有所有权:无法直接访问对象,需要先转换为shared_ptr(lock()方法)
- 解决循环引用:shared_ptr的循环引用会导致计数无法归0,内存泄漏,weak_ptr可以解决
int main() {
shared_ptr<Person> p = make_shared<Person>(1, "Lucy", 17);
weak_ptr<Person> wp = p;
weak_ptr<Person> wp2 = p;
// weak_ptr 不能直接访问内存空间
//wp->show();
// shared_ptr 引用计数为0时,weak_ptr则过期
// false 未过期, true 过期(引用计数为0)
cout << boolalpha << wp.expired() << endl;
cout << boolalpha << wp2.expired() << endl;
//p->show();
wp.lock()->show();
p.reset();
cout << boolalpha << wp.expired() << endl;
cout << boolalpha << wp2.expired() << endl;
return 0;
}
3.4 适用场景
- 解决 shared_ptr 循环引用(最核心用途):A 存 shared_ptr<B>,B 存 weak_ptr,打破环形引用;
- 缓存 / 观察者模式:缓存池用 weak_ptr 监听资源,资源被释放后缓存自动失效;
- 只想查看资源、不想持有资源生命周期,需要用时
lock()提升为 shared_ptr 临时占用;
不适合
单独管理堆内存、独占资源。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)