文章目录
- Day12-1 智能指针回顾
- 一、问题回顾
- 二、智能指针
- 1. `unique_ptr`
- 构建右值的两种方式:
- 构建左值的两种方式:
- 2. `shared_ptr`
- 3. 循环引用与 `weak_ptr`
- 4. `weak_ptr`
- 5.检查 `weak_ptr` 管理的对象是否还存在的方法
- 三、删除器(Deleter)
- 🌟 `std::default_delete`
- 函数形式:
- ✨ 自定义删除器示例(以 `FILE*` 为例)
- ✅ 推荐用法(自动释放):
- 四、智能指针的误用(⚠️易错点)
- 🔥 示例类 Point
- ❌ `unique_ptr` 的误用
- ❌ `shared_ptr` 的误用
- ✅ 正确使用 `shared_from_this`
- ✅ 正确的 `addPoint` 函数:
Day12-1 智能指针回顾
一、问题回顾
- 什么是右值引用?有什么特点?右值引用本身是左值还是右值?
- 左值是什么?右值是什么?
- 移动构造函数与移动赋值函数的形态是什么样的?
std::move函数的实质是什么?是否具备移动的含义?- RAII是什么?具有什么特征?
- 对象语义与值语义的区别是什么?
auto_ptr有什么缺陷?
二、智能指针
1. unique_ptr
- 独占所有权,不支持拷贝,只支持移动。
- 可以作为容器元素,因为具备移动语义。
#include <iostream>
#include <memory>
#include <vector>
using namespace std;void test()
{unique_ptr<int> up(new int(10));cout << "*up = " << *up << endl;// unique_ptr<int> up2 = up; // 错误:拷贝构造被禁用// up3 = up; // 错误:拷贝赋值被禁用unique_ptr<int> up3(new int(34));vector<unique_ptr<int>> vec;// vec.push_back(up); // 错误:不能拷贝vec.push_back(std::move(up)); // OK:使用移动语义vec.push_back(std::move(unique_ptr<int>(new int(20)))); // OKfor (const auto& value : vec) {cout << *value << endl;}
}
构建右值的两种方式:
- 显式调用构造函数创建临时对象
- 使用
std::move将左值转为右值
构建左值的两种方式:
- 构造函数创建有名对象:
Point pt(1, 2); - 用右值引用绑定右值:
Point&& rref = Point(1, 2);
//延长number的生命周期static int number = 10;
void func()
{//加大括号,缩短number的生命周期{int number = 10;//作用域cout << "number = " << number << endl;}int a = 20;cout << "a = " << a << endl;//业务逻辑
}
2. shared_ptr
- 支持共享所有权,通过引用计数实现。
- 支持拷贝和移动,可以安全地放入容器中。
#include <iostream>
#include <memory>
#include <vector>
using namespace std;class Point
{
public:Point(int ix = 0, int iy = 0) : _ix(ix), _iy(iy) {cout << "Point(int, int)" << endl;}~Point() {cout << "~Point()" << endl;}Point(const Point& rhs) : _ix(rhs._ix), _iy(rhs._iy) {cout << "Point(const Point&)" << endl;}Point& operator=(const Point& rhs) {cout << "Point& operator=(const Point&)" << endl;if (this != &rhs) {_ix = rhs._ix;_iy = rhs._iy;}return *this;}private:int _ix, _iy;
};void test()
{shared_ptr<int> sp(new int(10));cout << "*sp = " << *sp << ", use_count = " << sp.use_count() << endl;shared_ptr<int> sp2 = sp; // 拷贝共享所有权cout << "*sp2 = " << *sp2 << ", use_count = " << sp2.use_count() << endl;shared_ptr<int> sp3(new int(34));sp3 = sp; // 也可以赋值共享cout << "*sp3 = " << *sp3 << ", use_count = " << sp3.use_count() << endl;// 放入容器shared_ptr<Point> sp4(new Point(1, 2));vector<shared_ptr<Point>> vec;vec.push_back(std::move(sp4));vec.push_back(shared_ptr<Point>(new Point(3, 4)));shared_ptr<Point> sp5(new Point(7, 8));//实际上在push_back中传左值或者传右值都ok!vec.push_back(sp5);// 总结:shared_ptr 拥有拷贝和移动语义
}
3. 循环引用与 weak_ptr
- 问题:循环引用会造成内存泄漏。
- 解决方案:用
shared_ptr搭配weak_ptr,避免引用计数无法归零。
#include <iostream>
#include <memory>
using namespace std;class Child; // 前向声明class Parent
{
public:Parent() { cout << "Parent()" << endl; }~Parent() { cout << "~Parent()" << endl; }shared_ptr<Child> pChild;
};class Child
{
public:Child() { cout << "Child()" << endl; }~Child() { cout << "~Child()" << endl; }weak_ptr<Parent> pParent; // 使用 weak_ptr 避免引用计数递增
};void test()
{shared_ptr<Parent> parentPtr(new Parent());shared_ptr<Child> childPtr(new Child());//解决内存泄漏的问题parentPtr->pChild = childPtr;childPtr->pParent = parentPtr;cout << "parentPtr.use_count() = " << parentPtr.use_count() << endl;cout << "childPtr.use_count() = " << childPtr.use_count() << endl;
}

4. weak_ptr
- 不能直接拥有资源,不增加引用计数。
- 常用于观察对象生命周期或解决循环引用问题。
#include <iostream>
#include <memory>
using namespace std;class Point
{
public:Point(int ix = 0, int iy = 0) : _ix(ix), _iy(iy) {cout << "Point(int,int)" << endl;}~Point() { cout << "~Point()" << endl; }Point(const Point& rhs) : _ix(rhs._ix), _iy(rhs._iy) {cout << "Point(const Point&)" << endl;}Point& operator=(const Point& rhs) {cout << "Point& operator=(const Point&)" << endl;if (this != &rhs) {_ix = rhs._ix;_iy = rhs._iy;}return *this;}private:int _ix, _iy;
};void test()
{/*weak_ptr必须从一个shared_ptr或者另一个weak_ptr来构造。weak_ptr本身并没有获得资源的所有权,所以它不能直接持有通过new创建的对象的所有权,因为它不会增加引用计数,也无法管理对象的释放。*///std::weak_ptr<Point> wp2(new Point(3, 4));//weak_ptr创建的时候,不能直接与托管资源产生联系std::weak_ptr<Point> wp;//空的 weak_ptr;可以创建空对象std::shared_ptr<Point> sp(new Point(1, 2));wp = sp;// weak_ptr 可以通过 shared_ptr 构造std::unique_ptr<Point> pt;// 不允许用 unique_ptr 构造 weak_ptr
}
int main()
{test();return 0;
}
5.检查 weak_ptr 管理的对象是否还存在的方法
| 方法名 | 功能简介 |
|---|---|
use_count() | 返回共享对象的引用计数(观察 shared_ptr 是否还存在) |
expired() | 判断对象是否已销毁,相当于 use_count() == 0 |
lock() | 尝试将 weak_ptr 升级为 shared_ptr,若对象已销毁则返回空指针 |
示例代码:
#include <iostream>
#include <memory>
using namespace std;int main() {shared_ptr<int> sp = make_shared<int>(42);weak_ptr<int> wp = sp;cout << "use_count = " << wp.use_count() << endl; // 输出 1cout << "expired = " << boolalpha << wp.expired() << endl; // 输出 falseif (shared_ptr<int> temp = wp.lock()) {cout << "对象存在,值为:" << *temp << endl;} else {cout << "对象已销毁" << endl;}sp.reset(); // 手动释放 shared_ptrcout << "\n释放后:\n";cout << "use_count = " << wp.use_count() << endl; // 输出 0cout << "expired = " << wp.expired() << endl; // 输出 trueif (shared_ptr<int> temp = wp.lock()) {cout << "对象存在,值为:" << *temp << endl;} else {cout << "对象已销毁" << endl;}return 0;
}
三、删除器(Deleter)
🌟 std::default_delete
std::default_delete 是 unique_ptr 和 shared_ptr 默认使用的删除器。其重载了 operator(),分别适用于普通指针和数组指针:

函数形式:
// 1. 删除普通对象
void operator()(T* ptr) const;// 2. 删除数组对象(只有当 T 是数组类型时)
template<class U>
void operator()(U* ptr) const;
- (1) 调用
delete ptr; - (2) 调用
delete[] ptr;,仅当 U()[] 可以隐式转换为 T()[] 时生效。 - 如果 U 是不完整类型(incomplete type),程序会报错。
- 无异常抛出保证(noexcept)
✨ 自定义删除器示例(以 FILE* 为例)
说明:
FILE* 不能使用默认的 delete 释放,必须使用 fclose。所以我们定义一个自定义删除器 FileCloser:
✅ 推荐用法(自动释放):
#include <memory>
#include <cstdio>
#include <iostream>
#include <string>struct FileCloser {void operator()(FILE* fp) const {if (fp) {fclose(fp);std::cout << "fclose(fp)" << std::endl;}}
};void test() {std::string msg = "hello, world\n";std::unique_ptr<FILE, FileCloser> uptr(fopen("wd.txt", "a+"));if (uptr) {fwrite(msg.c_str(), 1, msg.size(), uptr.get());}// 自动调用 FileCloser 析构,自动 fclose
}void test2() {std::string msg = "hello, world\n";std::shared_ptr<FILE> sptr(fopen("wd.txt", "a+"), FileCloser());//先创建对象再传递参数的方法也是ok的,区别在于一个是传右值,另一个是传右值/*FileCloser fc;std::shared_ptr<FILE> sptr(fopen("wd.txt", "a+"), fc);*/if (sptr) {fwrite(msg.c_str(), 1, msg.size(), sptr.get());}// 自动调用 FileCloser 析构,自动 fclose
}
四、智能指针的误用(⚠️易错点)
🔥 示例类 Point
class Point : public std::enable_shared_from_this<Point> {
public:Point(int ix = 0, int iy = 0) : _ix(ix), _iy(iy) {std::cout << "Point(int,int)" << std::endl;}~Point() { std::cout << "~Point()" << std::endl; }Point(const Point& rhs) : _ix(rhs._ix), _iy(rhs._iy) {std::cout << "Point(const Point&)" << std::endl;}Point& operator=(const Point& rhs) {std::cout << "Point& operator=(const Point&)" << std::endl;if (this != &rhs) {_ix = rhs._ix;_iy = rhs._iy;}return *this;}// ⚠️ 原始写法(误用):返回 this 的裸指针Point* addPoint(Point* pt) {_ix += pt->_ix;_iy += pt->_iy;return this;}void print() const {std::cout << "(" << _ix << ", " << _iy << ")" << std::endl;}private:int _ix, _iy;
};
❌ unique_ptr 的误用
void test1() {Point* pt = new Point(1, 2);std::unique_ptr<Point> up(pt);std::unique_ptr<Point> up2(pt); // ❌ 错误!双重释放
}void test2() {std::unique_ptr<Point> up(new Point(1, 2));std::unique_ptr<Point> up2(new Point(3, 4));up.reset(up2.get()); // ❌ 错误!up 和 up2 同时管理同一指针
}
❌ shared_ptr 的误用
void test3() {Point* pt = new Point(1, 2);std::shared_ptr<Point> sp(pt);std::shared_ptr<Point> sp2(pt); // ❌ 错误!会导致两次 delete
}void test4() {std::shared_ptr<Point> sp(new Point(1, 2));std::shared_ptr<Point> sp2(new Point(3, 4));sp.reset(sp2.get()); // ❌ 错误!sp 和 sp2 同时管理同一资源
}
✅ 正确使用 shared_from_this
void testAdd() {std::shared_ptr<Point> sp(new Point(1, 2));std::shared_ptr<Point> sp2(new Point(3, 4));std::shared_ptr<Point> sp3(sp->addPoint(sp2.get()));sp3->print();
}
⚠️ 这需要
addPoint()返回一个shared_ptr<Point>,否则sp3会重复管理裸指针(this)。
✅ 正确的 addPoint 函数:
// 改为返回 shared_ptr 并使用 shared_from_this()
std::shared_ptr<Point> addPoint(Point* pt) {_ix += pt->_ix;_iy += pt->_iy;return shared_from_this(); // 转换为 shared_ptr,安全共享
}
