博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
智能指针之make_unique与make_shared
阅读量:4299 次
发布时间:2019-05-27

本文共 3411 字,大约阅读时间需要 11 分钟。

make_unique的实现

std::make_shared是C++11的一部分,但是std::make_unique很可惜不是。它是在C++14里加入标准库的,但我们可以自己实现make_unique方法。

// 支持普通指针template
inlinetypename enable_if
::value,unique_ptr
>::typemake_unique(Args&&... args){ return unique_ptr
(new T(std::forward
(args)...));}// 支持动态数组template
inlinetypename enable_if
::value && extent
::value == 0,unique_ptr
>::typemake_unique(size_t size){ typedef typename remove_extent
::type U; return unique_ptr
(new U[size]());}// 过滤掉定长数组的情况template
typename enable_if
::value != 0,void>::typemake_unique(Args&&...) = delete;

enable_if的作用

// Primary template./// Define a member typedef @c type only if a boolean constant is true.template
struct enable_if { };// Partial specialization for true.template
struct enable_if
{ typedef _Tp type; };

结合源码可知,当condition==true时,enable_if<condition,T>::type ≡ T,否则报错。

  • enable_if<!is_array<T>::value,unique_ptr<T>>::typeconditionT不是数组类型时为true
  • enable_if<is_array<T>::value && extent<T>::value == 0,unique_ptr<T>>::typeconditionT为数组类型且数组中元素个数为0时为true,由于对于非数组类型extent<U>::value也为0,语句is_array<T>::value是必要的
  • enable_if<extent<T>::value != 0,void>::typeconditionT类型中元素个数不为0时为true,即T为定长数组

std::forward的作用

std::forward在这里的作用是实现参数的完美转发,具体见。

make函数的好处

1. 效率更高

shared_ptr需要维护引用计数的信息。如果你通过使用原始的new表达式分配对象,然后传递给shared_ptr(也就是使用shared_ptr的构造函数)的话,shared_ptr的实现没有办法选择,而只能单独的分配控制块:

这里写图片描述

如果选择使用make_shared的话,情况就会变成下面这样:

这里写图片描述

内存分配的动作,可以一次性完成。这减少了内存分配的次数,而内存分配是代价很高的操作。

2. 异常安全

看看下面的代码:

void F(const std::shared_ptr
& lhs, const std::shared_ptr
& rhs) { /* ... */ }F(std::shared_ptr
(new Lhs("foo")), std::shared_ptr
(new Rhs("bar")));

C++是不保证参数求值顺序,以及内部表达式的求值顺序的,所以可能的执行顺序如下:

  1. new Lhs(“foo”))
  2. new Rhs(“bar”))
  3. std::shared_ptr
  4. std::shared_ptr

假设在第2步的时候,抛出了一个异常(比如out of memory,总之,Rhs的构造函数异常了),那么第一步申请的Lhs对象内存泄露了。这个问题的核心在于,shared_ptr没有立即获得裸指针。

我们可以用如下方式来修复这个问题:

auto lhs = std::shared_ptr
(new Lhs("foo"));auto rhs = std::shared_ptr
(new Rhs("bar"));F(lhs, rhs);

当然,推荐的做法是使用std::make_shared来代替:

F(std::make_shared
("foo"), std::make_shared
("bar"));

std::make_shared被调用,指向动态内存对象的原始指针会被安全的保存在返回的std::shared_ptr对象中,然后另一std::make_shared被调用。如果此时产生了异常,那std::shared_ptr析构会知道于是它所拥有的对象会被销毁。

使用std::make_unique来代替new在写异常安全的代码里和使用std::make_shared一样重要。

make函数的不足

  • make函数都不允许使用定制删除器,但是std::unique_ptrstd::shared_ptr的构造函数都可以。
  • make函数不能完美传递一个initializer_list
    替代方案:
// initializer_list
aa = {1,2,3}; // 或者auto aa = {
1,2,3};auto a = make_shared
>(aa);// auto b = make_shared
>({1,2,3}); // 错误
  • 对象的内存可能无法及时回收

虽然使用std::make_shared可以减少了内存分配的次数,提高效率,但由于控制块与对象都在同一块动态分配的内存上,所以当对象的引用计数变为0,对象被销毁(析构函数被调)后,该对象所占内存仍未释放,直到控制块同样也被销毁,内存才会释放。

我们知道,在控制块中包含两个计数:shared countweak count,分别表示std::shared_ptrstd::weak_ptr对对象的引用计数,只有当shared countweak count都为0时,控制块才会被销毁。

换句话说,只要有std::weak_ptr指向一个控制块(weak count大于0),那控制块就一定存在。只要控制块存在,包含它的内存必定存在。通过std::shared_ptrmake函数分配的内存在最后一个std::shared_ptr和最后一个std::weak_ptr被销毁前不能被释放。

  • 构造函数是保护或私有时,无法使用make_shared
    替代方案:
class A {public:    static std::shared_ptr create() {        return std::make_shared();    }protected:    A() {}    A(const A &) = delete;    const A &operator=(const A &) = delete;};std::shared_ptr foo() {    return A::create();}

参考链接

你可能感兴趣的文章
python监控mysql连接数 批量杀进程 解决too many connections问题
查看>>
MySQL show processlist过滤
查看>>
mac os本地通过跳板机可视化连接服务器的mysql、mongodb、redis
查看>>
Python创建不可变的类实例
查看>>
Ajax发送、上传文件,Django后端接收、获取文件
查看>>
requests模块访问api接口小记,requests.request、requests.get和requests.post
查看>>
Python解决中文和空格对齐问题
查看>>
用Python的mutagen模块获取MP3音频文件的时长
查看>>
利用Python的pydub模块将二进制音频保存成普通的MP3、WAV文件
查看>>
解决安装oh-my-zsh后workon切换虚拟环境命令不起作用
查看>>
Python日志logging的levelname格式化参数1.1s小记
查看>>
pytest-cov插件计算单元测试代码覆盖率
查看>>
ubuntu虚拟机VMware桥接模式无法自动化获取IP的解决方法
查看>>
Python debug 报错:SystemError: unknown opcode
查看>>
Python将树结构转换成字典形式的多级菜单结构,写入json文件
查看>>
基于Python镜像制作讯飞语音实时识别docker镜像记录
查看>>
关闭linux防火墙让windows宿主机访问ubuntu虚拟机web服务以及docker
查看>>
pycharm 找不到同目录文件,但是终端中正常的小记
查看>>
安装了grpc但是无法导入:ImportError: No module named 'grpc'
查看>>
Python3调用腾讯实时语音识别示例
查看>>