文章目录
-
- [Day12-1 智能指针回顾](#Day12-1 智能指针回顾)
-
- 一、问题回顾
- 二、智能指针
-
- [1. `unique_ptr`](#1.
unique_ptr
) - [2. `shared_ptr`](#2.
shared_ptr
) - [3. 循环引用与 `weak_ptr`](#3. 循环引用与
weak_ptr
) - [4. `weak_ptr`](#4.
weak_ptr
) - [5.检查 `weak_ptr` 管理的对象是否还存在的方法](#5.检查
weak_ptr
管理的对象是否还存在的方法)
- [1. `unique_ptr`](#1.
- 三、删除器(Deleter)
-
- [🌟 `std::default_delete`](#🌟
std::default_delete
) - [✨ 自定义删除器示例(以 `FILE*` 为例)](#✨ 自定义删除器示例(以
FILE*
为例)) -
- [✅ 推荐用法(自动释放):](#✅ 推荐用法(自动释放):)
- [🌟 `std::default_delete`](#🌟
- 四、智能指针的误用(⚠️易错点)
-
- [🔥 示例类 Point](#🔥 示例类 Point)
- [❌ `unique_ptr` 的误用](#❌
unique_ptr
的误用) - [❌ `shared_ptr` 的误用](#❌
shared_ptr
的误用) - [✅ 正确使用 `shared_from_this`](#✅ 正确使用
shared_from_this
) -
- [✅ 正确的 `addPoint` 函数:](#✅ 正确的
addPoint
函数:)
- [✅ 正确的 `addPoint` 函数:](#✅ 正确的
Day12-1 智能指针回顾
一、问题回顾
- 什么是右值引用?有什么特点?右值引用本身是左值还是右值?
- 左值是什么?右值是什么?
- 移动构造函数与移动赋值函数的形态是什么样的?
std::move
函数的实质是什么?是否具备移动的含义?- RAII是什么?具有什么特征?
- 对象语义与值语义的区别是什么?
auto_ptr
有什么缺陷?
二、智能指针
1. unique_ptr
- 独占所有权,不支持拷贝,只支持移动。
- 可以作为容器元素,因为具备移动语义。
cpp
#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)))); // OK
for (const auto& value : vec) {
cout << *value << endl;
}
}
构建右值的两种方式:
- 显式调用构造函数创建临时对象
- 使用
std::move
将左值转为右值
构建左值的两种方式:
- 构造函数创建有名对象:
Point pt(1, 2);
- 用右值引用绑定右值:
Point&& rref = Point(1, 2);
cpp
//延长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
- 支持共享所有权,通过引用计数实现。
- 支持拷贝和移动,可以安全地放入容器中。
cpp
#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
,避免引用计数无法归零。
cpp
#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
- 不能直接拥有资源,不增加引用计数。
- 常用于观察对象生命周期或解决循环引用问题。
cpp
#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 ,若对象已销毁则返回空指针 |
示例代码:
cpp
#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; // 输出 1
cout << "expired = " << boolalpha << wp.expired() << endl; // 输出 false
if (shared_ptr<int> temp = wp.lock()) {
cout << "对象存在,值为:" << *temp << endl;
} else {
cout << "对象已销毁" << endl;
}
sp.reset(); // 手动释放 shared_ptr
cout << "\n释放后:\n";
cout << "use_count = " << wp.use_count() << endl; // 输出 0
cout << "expired = " << wp.expired() << endl; // 输出 true
if (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()
,分别适用于普通指针和数组指针:
函数形式:
cpp
// 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
:
✅ 推荐用法(自动释放):
cpp
#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
cpp
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
的误用
cpp
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
的误用
cpp
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
cpp
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
函数:
cpp
// 改为返回 shared_ptr 并使用 shared_from_this()
std::shared_ptr<Point> addPoint(Point* pt) {
_ix += pt->_ix;
_iy += pt->_iy;
return shared_from_this(); // 转换为 shared_ptr,安全共享
}