现代C++:智能指针
# 现代C++:智能指针
# 共享指针shared_ptr
共享指针会记录有多少个共享指针指向同一个物体,当数量为0便会自动释放内存。
# 使用共享指针
#include <memory>
using std::shared_ptr;
// 方法一【推荐】
// 使用make_shared动态分配内存
shared_ptr<int> p;
p = make_shared<int>(100);
// 方法二
// 使用new初始化
shared_ptr<int> p {new int(100)};
shared_ptr<int> p2 = p;
// ...DO SOMETHING
# 常用方法说明
# use_count()
使用
use_count()
可以查看有多少个shared_ptr
指向某个物体;
# reset()
使用
reset()
会重置,不再指向原来的物体
# 自定义删除函数
默认使用delete
来释放内存,同时可以使用自定义删除方式,如下是管理文件指针的示例:
void close_file(FILE* fp)
{
if (fp == nullptr) return;
fclose(fp);
cout << "File Closed." << endl;
}
int main()
{
FILE* fp = fopen("filepath.txt","w");
// 管理文件指针,不是销毁指针变量,而是要关闭文件操作
shared_ptr<FILE> sfp {fp, close_file};
if (sfp == nullptr)
cerr << "Error opening file." << endl;
else
cout << "File opened." << endl;
return 0;
}
# 弱引用指针 weak_ptr
如下相互引用示例中,使用 shared_ptr
将会导致内存泄漏的情况
struct A;
struct B;
struct A {
std::shared_ptr<B> ptrB;
~A() {
std::cout << "A 被销毁" << std::endl;
}
};
struct B {
// std::weak_ptr<A> ptrA;
std::shared_ptr<A> ptrA;
~B() {
std::cout << "B 被销毁" << std::endl;
}
};
void weak_ptr_test()
{
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->ptrB = b;
b->ptrA = a;
}
由于内部成员变量之间存在相互引用的情况,a,b引用计数均为2,当离开作用域,引用计数只能减1,然而外部已经无法找到该块内存,也就造成了内存泄漏,示意图如下:
解决这个问题的办法就是使用弱引用指针 std::weak_ptr
,std::weak_ptr
是一种弱引用,它不会引起引用计数增加,当换用弱引用时候,最终的释放流程如图:
在倒数第二步中由于B中指向A的引用为弱引用,不计入引用计数,因而B会被释放,此时A也没有指向的对象,因此这块内存资源也会被释放。
# 独享指针 unique_ptr
相比于
shared_ptr
,独享指针是一种零开销的智能指针独享,因而不能有两个独享指针同时指向一份资源,也不支持复制操作
# 使用独享指针
#include <memory>
using namespace std;
// 方法一【推荐】
// 使用make_unique动态分配内存
unique_ptr<int> p {make_unique<int>(100)};
// 方法二
// 使用new初始化
unique_ptr<int> p {new int(100)};
unique_ptr<int> p2 = p;
// ...DO SOMETHING
# 常用方法说明
# reset()
释放
unique_ptr
下的资源
# 内存管理
默认情况下使用new和delete来分配和释放内存,
可以自定义分配函数和释放函数
// ...
int* my_alloc(int v)
{
cout << "Allocating " << v << endl;
return new int{v};
}
void my_dealloc(int* p)
{
cout << "Deallocating " << *p << endl;
delete p;
}
int main()
{
unique_ptr<int, decltype(&my_dealloc)> cup {my_alloc(100), my_dealloc};
return 0;
}
# 如何在函数间传递unique_ptr
由于独享指针不能复制操作这一特点,在函数传参时会进行复制操作进而编译报错,以下是几个解决方案
# 如果是访问独享指针指向的内容
void function1(int& value){}
void function2(int* p){}
void main()
{
auto up = make_unique<int>(123);
// 方法一
// 传递所管理的资源给int引用
function1(*up);
// 方法二
// 使用裸指针避免复制
function2(up.get());
}
# 如果需要对独享指针指向的内容进行操作
void function3(unique_ptr<int>& up){}
void function4(unique_ptr<int> up){}
void main()
{
auto up = make_unique<int>(123);
// 方法一
// 函数参数设置为unique_ptr的引用
function3(up);
// 方法二
// 使用std::move()
// 注意:转移控制权后原up被设置为nullptr
function4(std::move(up));
}
# 函数返回unique_ptr
unique_ptr<int> return_uptr(int value)
{
unique_ptr<int> up = make_unique<int>(value);
return up; // 这里原本是std::move(up),编译器自动替换
}
void main()
{
unique_ptr<int> up = return_uptr(321);
cout << "up: " << *up << endl;
}
编辑 (opens new window)
上次更新: 2023/08/16, 16:01:49