chapter4 解决 内存泄露: 智能指针
2022-06-18 本文已影响0人
my_passion
SP
internal ptr 指向 heap 内存
`控制 lifetime`
确保 离开 SP scope 时, 自动 delete internal heap 内存, 防止内存泄露
通用技术
`引用计数 RC`
SP 每 使用 1次, 其 internal RC 加 1;
每 析构 1次, 其 internal RC 减 1; 减为 0 时, delete 所指 heap 内存
<memory>
4.1 std::shared_ptr
(1) 初始化
——————————————————————————————————————————————————
[1] Ctor
——————————————————————————————————————————————————
[2] std::make_shared 高效
——————————————————————————————————————————————————
[3] reset() shared_ptr 未初始化 => RC = 1
shared_ptr 已初始化 => RC - 1
——————————————————————————————————————————————————
template <class T, class... Args>
shared_ptr<T>
make_shared (Args&&... args);
1] ::new 分配 T 对象 memory + args 作 参数 调 T Ctor
|
| ptr 作 SP internal ptr
|/
2] 分配 shared_ptr 对象 memory
3] RC = 1
std::shared_ptr<int> sp1 = std::make_shared<int> (1);
std::shared_ptr<int> sp2( new int(1) );
std::shared_ptr<int> sp3;
sp3.reset( new int(1) );
不能将 原始指针 赋值给 SP, 编译报错: 语法错
std::shared_ptr<int> sp4 = new int(1);
std::make_shared 相比 std::shared_ptr Ctor 的 好处
优点
[1] std::shared_ptr Ctor: 2次内存分配
Resource <- - - -
|
|
control block: resPtr + strongPtrNum/RCNum + weakPtrNum + Deleter + Allocator
|
| make_shared: 1次内存分配 => 时 空 效率更高
|/
Resource + control block: strongPtrNum/RCNum + weakPtrNum + ...
[2] 异常安全
潜在的资源泄露
f1(std::shared_ptr<A>(new A), f2() );
可能的执行顺序
new A
f2()
std::shared_ptr Ctor
若 f2() 抛出异常, 则 第1步 new 的 内存泄露
|
| 异常安全
|/
f1(std::make_shared<A>(), f2() );
缺点
[1] resource 的 Ctor private/protected 时, make_shared 不能用
[2] 延迟了 resource 内存 回收 的时间
last weak_ptr 出 scope 时, resource 内存才被回收
原本 强引用(RC) 减为 0 时, resource 内存回收
现在变为了 强/弱引用 都减为 0 时, resource 内存回收
(2) operator=
Replaces the managed object with the one managed by r.
对 原 resource 的 RC 减 1, 减到 0 则 delete 原 resource
对 新 resource, 若存在, 其 RC 加 1
(3) 获取 internal ptr: get()
int* p = sp.get();
(4) 到 bool 的 类型转换运算符: operator bool()
<=> get() != nullptr;
#include <memory>
#include <iostream>
void f(std::shared_ptr<int> sp)
{
if (sp) // operator bool():
std::cout << "*sp=" << *sp << "\n";
}
int main()
{
std::shared_ptr<int> sp = std::make_shared<int>(1);
f(sp);
}
(5) 指定 deleter(删除器)
resource's RC = 0 时, 自动调用 deleter
[1]
// class A;
std::shared_ptr<A> sp(new A);
|
| 等价
|
std::shared_ptr<A> sp(new A, [](A* p) { delete p; } )
|
| 问题: std::shared_ptr 的默认 deleter 不支持 数组对象
|/
[2] 用 std::default_delete
std::shared_ptr<A> sp(new A[2], [](A* p) { delete[] p; } )
|
| 等价
|
std::shared_ptr<A> sp(new A[2], std::default_delete<A[]> );
|
| 封装
|/
template <typename T>
std::shared_ptr<T>
make_shared_array(size_t n)
{
return std::shared_ptr<T>(new T[n], std::default_delete<T[]>() );
}
#include <memory>
// === 不支持 internal ptr 指向 1维数组
template <typename T>
class Deleter
{
public:
void operator()(T* p) const
{
delete p;
}
};
template <typename T>
std::shared_ptr<T>
make_shared_array(size_t n)
{
return std::shared_ptr<T>(new T[n], std::default_delete<T[]>() );
}
int main()
{
// std::shared_ptr<int> sp(new int[2], Deleter<int[] >()); // compile error
std::shared_ptr<int> sp1(new int, Deleter<int>() ); // ok
std::shared_ptr<int> sp2(new int[2], std::default_delete<int[]>() );
std::shared_ptr<int> sp3 = make_shared_array<int>(2);
}
(6) operator* operator->
(7) use_count() : resource's RC
(8) Note
[1] 1 个 native ptr 不能 初始化 多个 shared_ptr
|
|/
RC 不正确
int* p = new int;
std::shared_ptr<int> sp1(p);
std::shared_ptr<int> sp2(p); // logic error
[2] 不要在 `函数实参` 中 `创建 shared_ptr`
void f(shared_ptr<int>(new int(1) ), g() );8
编译器 重排 同一语句内 各基本操作的 顺序
=> new 与 shared_ptr 间 其他 操作 抛出异常
=> 内存泄露
|
| 解决
|/
先创建 shared_ptr, 再 函数实参
f1(std::shared_ptr<A>(new A), f2() );
|
|/
std::shared_ptr<A> sp1(new A);
f1(sp1, f2() );
[3] this 指针 ( 作实参 构造的 shared_ptr ) 不能直接 return
|
| 本质是 native ptr
|/
同一 this 指针 构造 2个 shared_ptr 没有关联
this 被 delete 2次
|
| 解决
|/
std::enable_shared_from_this<> 内部的
weak_ptr 通过 已构造的 sp 构造 newSp 返回
=> weak_ptr 关联起 已构造的 sp 与 newSp
// 运行报错
#include <memory>
#include <iostream>
struct A
{
std::shared_ptr<A> getSelfSp()
{
return std::shared_ptr<A>(this); // don't do it
}
};
int main()
{
std::shared_ptr<A> sp1(new A);
std::shared_ptr<A> sp2 = sp1->getSelfSp();
}
|
|/
#include <memory>
#include <iostream>
struct A: public std::enable_shared_from_this<A>
{
std::shared_ptr<A> getSelfSp()
{
return shared_from_this();
}
~A() { std::cout << "A dtor \n"; }
};
int main()
{
std::shared_ptr<A> sp1(new A);
// 获取 自身智能指针的函数 仅在 sp Ctor 被调用之后才能用
std::shared_ptr<A> sp2 = sp1->getSelfSp();
}
[4] 避免 循环引用
2 个 struct (A B)
|
| 都有1个 `指向对方类型(A B) 的 shared_ptr` 成员
| |
|/ |
分别被 |/
`2 个 shared_ptr` 管理 实际 也指向 `对方 obj`
=> 2个 resource 的 RC 均= 2
2 个 shared_ptr 离开 scope 时, 2份 resource’s RC 只减 到 1, 而不是 0
=> 2 份 resource(A B) 都不会被 析构: 内存泄露
|
| 解决
|/
A B 之一 的 成员 shared_ptr 改为 weak_ptr
如 B 内部用 weak_ptr
=> resourceA/spA 的 RC = 1, resourceB/spB 的 RC = 2
spA 出 scope 时, spA 的 RC 减 1 变为 0 => resourceA 析构
=> 调 成员 sp<B> 的 dtor => resourceB/spB 的 RC 减 1 变为 1
spB 出 scope 时, spB 的 RC 减 1 变为 0 => resourceB 析构
#include <memory>
#include <iostream>
struct A;
struct B;
struct A
{
std::shared_ptr<B> spB;
~A() { std::cout << "A dtor \n"; }
};
struct B
{
std::shared_ptr<A> spA;
~B() { std::cout << "B dtor \n"; }
};
void test()
{
{
std::shared_ptr<A> spA(new A);
std::shared_ptr<B> spB(new B);
spA->spB = spB;
spB->spA = spA;
}
}
int main()
{
test();
}
// print: 无
|
|/
#include <memory>
#include <iostream>
struct A;
struct B;
struct A
{
std::shared_ptr<B> spB;
~A() { std::cout << "A dtor \n"; }
};
struct B
{
std::weak_ptr<A> wpA;
~B() { std::cout << "B dtor \n"; }
};
void test()
{
{
std::shared_ptr<A> spA(new A);
std::shared_ptr<B> spB(new B);
spA->spB = spB;
spB->wpA = spA;
}
}
int main()
{
test();
}
// print
A dtor
B dtor
4.2 std::unique_ptr 独占的 SP
与 shared_ptr 区别
(1) 独占性 & copy delete
std::unique_ptr<int> up1(new int);
std::unique_ptr<int> up2 = up1; // compile error
可匹配 move ctor/assignment
两侧均 左值 => 必须用 std::move() 显式 move
1侧为 右值: implicitly move
std::unique_ptr<int> up2 = std::move(up1);
(2) 可 指向 数组
std::unique_ptr<int []> up(new int[2]);
std::shared_ptr<int []> sp(new int[2]); // 编译报错
(3) C++11 还没提供 make_unique()
自定义不难
// version1: 支持 普通指针
template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T> >::type
make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)... ) );
}
(4) 可自定义 Deleter
——————————————————————————————————————————————————————————————————————
函数对象 | lambda
——————————————————————————————————————————————————————————————————————
| 不捕获 | 捕获
| 可转换为 函数指针 |
——————————————————————————————————————————————————————————————————————
第2模板参数 | Functor 类型 | 函数指针 类型 | std::function
——————————————————————————————————————————————————————————————————————
第2实参 | 不需要 | lambda | lambda
——————————————————————————————————————————————————————————————————————
// (1)
std::unique_ptr<int, void(*)(int*)> up(new int(1), [] (int* p) {delete p; } );
| |\
|_ _ _ _ _ _ _ _ _ _ _ _ _ _|
// (2)
std::unique_ptr<int, std::function<void (int*)> >
up(new int(1),
[&] (int* p) {delete p; } );
// (3)
#include <memory>
#include <iostream>
template <typename T>
class Deleter
{
public:
void operator()(T* p) const
{
std::cout << "deleter:\n";
delete p;
}
};
int main()
{
std::unique_ptr<int, Deleter<int> > up(new int(1) );
}
4.3 std::weak_ptr 弱引用 的 SP
resource/referenced object - -
| | |
own | mod RC | |
| | |
shared_ptr1-----shared_ptr12 |
| |
| | non-owning: not mod RC, 查 referenced object 的 use_count()
| |
weak_ptr - - - - - - - - - -
weak reference
weak 的含义
non-owning resource
不能操作 resource: 没重载 (2种)解引用运运算符 * ->
其 构造/析构 not modify resource RC
weak_ptr 必须转换为 shared_ptr 才能 access resource
(1) 目的
observe/track resource 是否被 destory
=> 可 解决 循环引用
(2) 用法
————————————————————————————————————————————
[0] Ctor 实参: shared_ptr
[1] use_count() 获得 所观测 resource's RC
[2] expired() <=> use_count() == 0
[3] lock() 获得 所监视的 shared_ptr
————————————————————————————————————————————
// sp1 赋值给 sp2, 两者的 weak_ptrs 均 == 1,
// 全局 weak_ptr 通过 sp1 观察 resource 后, 两者的 weak_ptrs == 2
#include <iostream>
#include <memory>
std::weak_ptr<int> gw;
void observeRefObj()
{
std::cout << "gw.use_count() == " << gw.use_count() << "; ";
// we have to make a copy of shared pointer before usage:
if (std::shared_ptr<int> sp = gw.lock())
{
std::cout << "gw.use_count() == " << gw.use_count() << "; ";
std::cout << "*sp == " << *sp << '\n';
}
else
std::cout << "weak_ptr's shared_ptr empty\n";
if ( gw.expired() )
std::cout << "resource destoryed\n";
else
std::cout << "resource not destoryed\n";
}
int main()
{
{
std::shared_ptr<int> sp1 = std::make_shared<int>(10);
auto sp2 = sp1;
gw = sp1;
observeRefObj();
}
observeRefObj();
}
// print
gw.use_count() == 2; gw.use_count() == 3; *sp == 10
resource not destoryed
gw.use_count() == 0; weak_ptr's shared_ptr empty
resource destoryed
4.4 通过 SP 管理第3方库 分配的内存
第 3 方库
内存分配 -> return native ptr
内存释放
用 第 3 方接口
void* p = GetHandle()->Create(); // Factory Method
// ...
GetHandle()->Release(p);
|
| 智能指针
|/
void* p = GetHandle()->Create();
std::shared_ptr<void> sp(p,
[this](void* p){ GetHandle()->Release(p); } )
参考
https://zhuanlan.zhihu.com/p/337656226
https://www.jianshu.com/p/03eea8262c11