7.5智能指针

7.5智能指针


永远不要将资源分配结果赋值给原始指针。无论使用哪种资源分配方法,都应当立即将资源指针存储在智能指针unique ptr或 shared ptr中,或使用其他RAII类。RAII代表Resource Acquisition IsInitialization(资源获取即初始化)。RAII 类获取某个资源的所有权,并在适当的时候进行释放。

1.unique_ptr

unique_ptr拥有资源的唯一所有权。当 unique_ptr被销毁或重置时,资源将自动释放。unique_ptr的一个优点是,内存和资源总会被释放,即使在执行return 语句或抛出异常时也是如此。例如,当函数有多个返回语句时,这简化了编码,因为不必记住在每个return语句之前释放资源。
作为经验法则,总是应该将动态分配的有唯一所有者的资源保存在 unique_ptr 的实例中。

1.1创建unique_ptr

auto uPtr {std::make_unique<T>(parameter)};

make_unique()使用值初始化,例如,基本类型被初始化为零,对象被默认构造。如果不需要此值初始化,例如在对性能有严格要求的代码中,可以使用C+20中引入的 make_unique_for_overwrite()函数,该函数使用默认初始化。对于基本类型,这意味着它们根本没有初始化,包含它们所在内存中的任何内容,而对象仍然是默认构造的。还可以通过以下语句创建:

std::unique_ptr<T> uPtr {new T{}};

1.2使用unique_ptr

get()方法用于直接获取底层指针

reset()可释放unique_ptr的底层指针,并根据需要将其变为另一个指针

uPtr.reset();
uPtr.reset(new T{});

可使用release()断开unique ptr与底层指针的连接。release()方法返回资源的底层指针,然后将智
能指针设置为 nullptr。实际上,智能指针失去对资源的所有权,你需要负责在用完资源时释放资源。

由于unique_ptr代表唯一拥有权,因此无法复制它!但是,可使用移动语义将一个unique_ptr
到另一个。std∶move()工具函数可以用于显式移动unique_ptr的所有权:

class Foo
{
  public:
    Foo(unique_ptr<int> data): m_data{move(data)}{}
  private:
    unique_ptr<int> m_data;
};

auto myIntSmartPtr {make_unique<int>(42)};
Foo f{move(myIntSmartPtr)};

1.3unique_ptr与C风格数组

1.4自定义deleter

int* my_alloc(int value){return new int{value};}
void my_free(int* p){delete p;}

int main()
{
    unique_ptr<int, decltype(&my_free)> myIntSmartPtr {my_alloc(42), my_free};
}

这项特性适用于当unique_ptr离开作用域时释放资源,如关闭文件或网络套接字。

2.shared_ptr

std::shared_ptr是一个可复制的支持共享所有权的智能指针,通过引用计数来解决资源释放的问题。

2.1创建并使用shared_ptr

auto sPtr {make_shared<T>()};

shared_ptr也支持get()reset()方法,只是当调用reset()时,仅在最后的shared_ptr销毁或重置时,才释放底层资源。shared_ptr不支持release()方法。

可使用use_count()方法检索共享同一资源的shared_ptr实例数。

自定义deleter语法如下:

shared_ptr<int> myIntSmartPtr {malloc_int(42), my_free};

2.2引用计数的必要性

作为一般概念,引用计数(reference counting)用于跟踪正在使用的某个类的实例或特定对象的个数。引用计数的智能指针跟踪为引用一个真实指针(或某个对象)而建立的智能指针的数目。每次复制这样一个引用计数的智能指针时,都会创建一个指向同一资源的新实例,并且引用计数会增加。当此类智能指针实例超出作用域或被重置时,引用计数将减少。当引用计数降至零时,资源不再有其他所有者,因此智能指针将释放资源。

唯一安全得到多个指向同一内存的 shared_ptr 实例的方法是创建 shared_ptr 的副本。

Simple* mySimple{ new Simple{} };
shared_ptr<Simple> sPtr1{mySimple};
shared_ptr<Simple> sPtr2{mySimple};//引用计数不会增加,会导致双重释放

2.3强制转换shared_ptr

const_pointer_cast()

dynamic_pointer_cast()

static_pointer_cast()

reinterpret_pointer_cast()

2.4别名

shared_ptr支持所谓的别名, 这允许一个shared_ptr与另一个shared_ptr共享一个指针(拥有的指针), 但是指向不同的对象(存储的指针). 这可用于使用一个shared_ptr拥有一个对象本身的同时, 指向该对象的成员:

class Foo{
  public:
  Foo(int value): m_data(value){}
  int m_data;
};

auto foo {make_shared<Foo>(42)};
auto aliasing {shared_ptr<int>{foo, &foo->m_data}};

仅当两个shared_ptr都销毁时, 才销毁对象Foo.

“拥有的指针”用于引用计数, 对指针解引用或调用它的get()时, 将返回“存储的指针”.

3.weak_ptr

weak_ptr 可包含由 shared_ptr管理的资源的引用。weak_ptr不拥有这个资源,所以不能阻止shared_ptr释放资源。weak_ptr销毁时(例如离开作用域时)不会销毁它指向的资源,然而,它可用于判断资源是否已经被关联的shared_ptr释放了。weak_ptr 的构造函数要求将一个shared_ptr 或另一个weak_ptr作为参数。为了访问weak_ptr中保存的指针,需要将 weak _ptr转换为 shared_ptr。这有两种方法∶

  • 使用 weak_ptr实例的lock()方法,这个方法返回一个shared_ptr。如果同时释放了与weak_ptr关联的 shared_ptr,返回的 shared_ptr是nullptr
  • 创建一个新的 shared_ptr 实例,将 weak_ptr 作为 shared_ptr 构造函数的参数。如果释放了与weak_ptr关联的 shared_ptr,将抛出 std::bad_weak_ptr异常。

4.向函数传递参数

仅当涉及所有权转移或所有权共享时,接受指针作为其参数之一的函数才应接受智能指针。要共享 shared_ptr的所有权,只需要接受按值传递的shared_ptr作为参数。类似地,要转移unique_ptr 的所有权,只需要接受按值传递的 unique_ptr 作为参数。后者需要使用移动语义,第9章将详细讨论。
如果既不涉及所有权转移也不涉及所有权共享,那么函数应该简单地使用 non-const 的引用或 const 引用作为参数;或者如果 nullptr是参数的有效值,则应该使用原始指针作为参数。拥有诸如const shared_ptr&或 const unique_ptr&的参数类型没有多大意义。

5.从函数中返回

标准智能指针shared_ptr、unique_ptr 和weak_ptr可以简单有效地从函数中按值返回.

6.enable_shared_from_this

std:enable_shared_from_this 派生的类允许对象调用方法,以安全地返回指向自己的 shared_ptr或
weak_ptr。如果没有这个基类,返回有效的 shared_ptr或weak_ptr的一种方法是将weak_ptr作为成员添加到类中,并返回它的副本或返回由它构造的 shared_ptr。enable_shared_from_this类给派生类添加了以下两个方法∶
shared_from_this()——返回一个shared_ptr,它共享对象的所有权。
weak_from_this()——返回一个 weak_ptr,它跟踪对象的所有权。

class Foo: public enable_shared_from_this<Foo>
{
  public:
  	shared_ptr<Foo> getPointer(){
      return shared_from_this();
    }
}

int main()
{
  auto ptr1 {make_shared<Foo>()};
  auto ptr2 {ptr1->getPointer()};
}

注意,仅当对象的指针已经存储在 shared_ptr时,才能使用对象上的 shared_from_this()。否则,将会抛出 bad_weak_ptr 异常。另一方面,调用weak_from_this()总是允许的,但当对象的指针还未存储在 shared_ptr时,将会返回一个空的 weak_ptr。

7.过时的、移除的auto_ptr

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值