计算机 · 2021年5月23日 0

深入应用C++11笔记 第4章

使用C++11解决内存泄漏的问题

shared_ptr

1.初始化

可以通过构造函数、std::make_shared<T>和reset方法来初始化shared_ptr

优先使用make_shared来构造shared_ptr,因为它更加高效。

不能将一个原始指针直接赋值给一个shared_ptr

std::shared_ptr<int> p = new int(); //错误

2.获取原始指针

3.指定删除器

void DeleteIntPtr(int* p)
{
  delete p;
}

std::shared_ptr<int> p(new int, DeleteIntPtr);

当用shared_ptr管理动态数组时,需要指定删除器,因为std::shared_ptr的默认删除器不支持数组对象。

std::shared_ptr<int> p(new int[10], [](int* p){detele[] p;});

也可以将std::default_detele作为删除器。default_delete通过调用delete来实现删除。

std::shared_ptr<int> p(new int[10], std::default_delete<int[]>);

为什么shared_ptr需要显示指定数组的删除器呢?大概是因为shared_ptr的模板参数类型是一个固定的Tshared_ptr的模板代码无法区分T*指向的是单个元素指针还是数组指针吧。

4.使用shared_ptr需要注意的地方

  • 不要用一个原始指针去初始化多个shared_ptr,这样多个shared_ptr管理同一个资源,析构的时候会多次删除这个对象。
  • 不要在函数实参中创建shared_ptr
function(shared_ptr<int>(new int), g());

像上面的代码,会先执行new int,但是shared_ptr<int>()g()的执行顺序是没有规定的,因此可能出现g()先执行,但是g()抛异常,然后shared_ptr未创建成功,new int的内存就泄漏了。

  • 通过shared_from_this返回this指针。

当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr

我们可能会想给类添加一个从this指针生成shared_ptr的成员函数,但是这会导致一些问题,因为我们可能会在代码中手动创建该类对象的shared_ptr,然后又调用该对象的生成shared_ptr的成员函数,这样这个对象就有了两个shared_ptr在管理它,就会出问题。

struct A {
  shared_tr<A> GetSelf() {
    return shared_ptr<A>(this);
  }
};

int main()
{
  shared_ptr<A> sp1(new A);
  shared_ptr<A> sp2 = sp1->GetSelf(); // 生成了两个shared_ptr管理同一个对象
  return 0;
}

正确的做法是继承std::enable_shared_from_this模板类。

class A: public std::enable_shared_from_this<A>
{
  std::shared_ptr<A> GetSelf() {
    return shared_from_this();
  }
};

std:shared_ptr<A> spy(new A);
std::shared_ptr<A> p = spy->GetSelf();

https://en.cppreference.com/w/cpp/memory/enable_shared_from_this

A common implementation for enable_shared_from_this is to hold a weak reference (such as std::weak_ptr) to this. The constructors of std::shared_ptr detect the presence of an unambiguous and accessible (ie. public inheritance is mandatory) (since C++17) enable_shared_from_this base and assign the newly created std::shared_ptr to the internally stored weak reference if not already owned by a live std::shared_ptr (since C++17). Constructing a std::shared_ptr for an object that is already managed by another std::shared_ptr will not consult the internally stored weak reference and thus will lead to undefined behavior.

It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr<T>. Otherwise the behavior is undefined (until C++17)std::bad_weak_ptr is thrown (by the shared_ptr constructor from a default-constructed weak_this) (since C++17).

enable_shared_from_this provides the safe alternative to an expression like std::shared_ptr<T>(this), which is likely to result in this being destructed more than once by multiple owners that are unaware of each other (see example below)

如上面所述,shared_from_this还只能被shared_ptr<T>这样的智能指针调用。

  • 通过使用weak_ptr避免shared_ptr的循环引用。

unique_ptr

unique_ptr不允许复制,但是可以通过函数返回给其他的unique_ptr,还可以通过std::move来转移到其他的unique_ptr,这样它本身就不再拥有原来指针的所有权了。

unique_ptr指定删除器时与shared_ptr不一样,需要指定删除器的类型。

std::unique_ptr<int, void(*)(int *)> ptr(new int(1), [](int *p){delete p;});

weak_ptr

  • 通过use_count()获取当前观测资源的引用计数
  • 通过expired()方法获取当前观测资源是否已经被释放
  • 解决循环引用
  • 通过shared_from_this返回this指针