Table of Contents
智能指针 - smart pointer
智能指针用来确保一个对象可在没有引用后被删除。
疑问暂存
为什么感觉和 rust 的 copy,move 语义很像; cpp-vs-rust-ownership
当 shared_ptr ref_count 计数到 0 开始调用析构的行为,与 GC 的对比
https://stackoverflow.com/questions/4663385/garbage-collection-vs-shared-pointers
最主要的区别在与资源被释放的时间;
- GC 可能发生在资源紧缺时,并且会要求程序暂停
- shared pointers 紧跟在 reference count 缩减到 0 时发生
GC 一定是存在于语言 runtime 上吗?
- https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals
- garbage collector for c and cpp: https://zh.wikipedia.org/wiki/貝姆垃圾收集器
作用
- 防止内存泄漏:无需手动释放,可自动释放堆上内存
- 共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题
C++11 支持如下三种智能指针:shared_ptr,unique_ptr, weak_ptr,其中三者特点体现在所有权问题上。
shared_ptr
shared_ptr 包含有两部分:
- 指向堆上对象的裸指针,raw_ptr
- 指向控制块,其中包含一个 reference count、一个 weak count
常用函数
- std::make_shared 创建对象 T,并由 std::shared_ptr 封装起来
- std::shared_ptr::get 返回存储的指针,
- std::shared_ptr::reset 重置管理的对象
- std::shared_ptr::use_count returns the number of shared_ptr objects referring to the same managed object
Returns the number of different shared_ptr instances (this included) managing the current object. If there is no managed object, 0 is returned.
返回 shared_ptr 的强引用计数 - std::shared_ptr::unique 若 use_count() 为 1 则返回 true,否则 false
#include <iostream>
#include <memory>
void fun(std::shared_ptr<int> sp) {
std::cout << "in fun(): sp.use_count() == " << sp.use_count() << " (object @ "
<< sp << ")\n";
}
int main() {
auto sp1 = std::make_shared<int>(5);
std::cout << "in main(): sp1.use_count() == " << sp1.use_count()
<< " (object @ " << sp1 << ")\n";
fun(sp1);
std::cout << "in main() after fun call: sp1.use_count() == " << sp1.use_count()
<< " (object @ " << sp1 << ")\n";
}
#+RESULTS:
| in | main(): | sp1.use_count() | == | 1 | (object | @ | 0x6000035c9158) | | | |
| in | fun(): | sp.use_count() | == | 2 | (object | @ | 0x6000035c9158) | | | |
| in | main() | after | fun | call: | sp1.use_count() | == | 1 | (object | @ | 0x6000035c9158) |
构造 shared_ptr
优先使用 make_shared 来构造,而无法将原始指针直接赋值,或者通过 reset 方式进行初始化:
audo sp1 = std:make_shared<int>;
sp1.reset(new int(666));
std::cout << "in main() after reset: sp.get() == " << sp1.use_count()
<< sp1.get() << " " << *sp1.get() << "\n";
谨慎使用原始指针
- 不要保存 std::shared_ptr::get 的返回值
- 保存为裸指针后,你无法判断它在何时将变为悬空指针
- 不要 delete p.get() 返回值,避免对同一内存地址释放两次
https://stackoverflow.com/questions/13223399/deleting-a-pointer-in-c
myPointer = new int;
delete myPointer; //freed memory
myPointer = NULL; //pointed dangling ptr to NULL
指定删除器
当用 shared_ptr 管理非 new 对象,或者无析构函数的类时,应为其传入 deleter
#include <iostream>
#include <memory>
using namespace std;
void DeleteIntPtr(int * p) {
cout << "Call DeleteIntPtr" << endl;
delete p;
}
int main()
{
std::shared_ptr<int> p(new int(1), Deleteintptr);
return 0;
}
注意问题
不要用一个原始指针初始化多个 shared_ptr
不要在函数实参中创建 shared_ptr
可能会因实参的求值顺序而导致错误,一般情况下是从右到左function(shared_ptr<int>(new int), g());
避免循环引用
循环引用会导致内存泄漏
#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
std::shared_ptr<B> bptr;
~A() { cout << "A is deleted" << endl; }
};
class B {
public:
std::shared_ptr<A> aptr;
~B() { cout << "B is deleted" << endl; }
};
int main()
{
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
} // ap, bp 离开了作用域名
cout << "ap and bp died" << endl;
return 0;
}
unique_ptr
https://en.cppreference.com/w/cpp/memory/unique_ptr
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope.The object is disposed of, using the associated deleter when either of the following happens:
the managing unique_ptr object is destroyed
the managing unique_ptr object is assigned another pointer via operator= or reset().There are two versions of std::unique_ptr:
Manages a single object (e.g. allocated with new)
Manages a dynamically-allocated array of objects (e.g. allocated with new[])
独占对象的智能指针
- 独占类型的智能指针,不能将其赋值给另一个 unique_ptr
- unique_ptr 可以指向一个数组
- unique_ptr 需要确定删除器的类型
当通过 move 转移给其他 unique_ptr 后,旧智能指针不再拥有原指针所有权了
如果只希望有一个智能指针管理资源或管理数组则使用 unique_ptr;
如果希望有多个智能指针管理同一个资源就用 shared_ptr;
weak_ptr
解决的问题
当两个对象互相使用 shared_ptr 成员变量指向对方时,造成的循环引用会使引用计数失效,从而
导致内存泄漏;
weak_ptr 是一种 不控制对象生命周期 的智能指针:
- 它指向一个 shared_ptr 所管理的对象
- 强引用的 shared_ptr 保持对该对象寿命周期的控制
- weak_ptr 只是提供了对管理对象的一个访问手段
设计目的
引入一种协助 shared_ptr 工作的智能指针,它只可以从一个 shared_ptr 或另一个
weak_ptr 对象构造,它的构造和析构不会引起引用计数的增加或减少
使用函数
- lock:创建一个 shared_ptr 对象来管理引用对象
- expired: 判断引用对象资源是否已经释放
#include <iostream>
#include <memory>
using namespace std;
std::weak_ptr<int> gw;
void observe()
{
cout << "gw.use_count() == " << gw.use_count() << endl;
if (shared_ptr<int> spt = gw.lock()) {
cout << "*spt == " << *spt << endl;
}
else {
cout << "gw is expired\n";
}
}
int main()
{
{
auto sp = make_shared<int>(42);
gw = sp;
observe();
}
observe();
}
#+RESULTS:
| gw.use_count() | == | 1 |
| *spt | == | 42 |
| gw.use_count() | == | 0 |
| gw | is | expired |