【问题标题】:Exception-aware malloc version of make_uniquemake_unique 的异常感知 malloc 版本
【发布时间】:2017-04-18 12:31:10
【问题描述】:

C++11 中没有make_unique(不能使用C++14),尽管可以很快像这样模拟(已经有很多答案建议这样):

template< typename T, typename ... Args >
std::unique_ptr< T > my_make_unique (Args && ... args) {
  return { new T( std::forward< Args >( args )... ) };
}

如果想要的结果是在 typename T 上调用 new / delete,这很好。在我的情况下,这不是因为我覆盖了全局new / delete,它在内部将(在方便的情况下)使用unique_ptr。因此,我使用malloc 和基于malloc 的各种包装器和分配器来使STL 容器在使用站点上工作而不会产生额外的膨胀。在出现异常之前,我在这种方法上做得很好......

基于this question 中的答案,到目前为止,我认为这是一个可能的通用解决方案:

template< typename T >
struct destroy_free {
  void operator() (void * p) {
    if ( !p ) return;
    static_cast< T* >( p )->~T();
    free( p );
  }
};
template< typename T, typename ... Args >
auto malloc_make_unique (Args && ... args) -> std::unique_ptr< T, destroy_free< T > > {
  if ( auto ptr = malloc( sizeof(T) ) )
    return { new ( ptr ) T( std::forward< Args >( args )... ) };
  return { nullptr }; // global new would throw bad_alloc here.
}

这似乎没问题,但这里完全忽略了异常处理,关于 new (ptr) T(...) 的工作方式,而不是 new T(...),以及这如何影响 make_unique 的任何版本,尤其是在使用自定义分配方法时。

首先,我知道投掷std::bad_alloc 是不明智的,但我相信最小意外原则适用于此,并且可以模仿new 的行为(即,在分配失败时投掷而不是返回nullptr)。这引出了两个问题。

1.用throw std::bad_alloc()代替return { nullptr }合理吗?

2。要正确复制new T(...)的行为,如果构造函数抛出了异常就需要被捕获,这样可以立即释放内存然后重新抛出构造函数异常?

假设两者都是,下面是正确处理这种情况还是有什么其他需要考虑的?

template< typename T, typename ... Args >
auto malloc_make_unique_v2 (Args && ... args) -> std::unique_ptr< T, destroy_free< T > > {
  if ( auto ptr = malloc( sizeof(T) ) ) try {
    return { new ( ptr ) T( std::forward< Args >( args )... ) };
  } catch( ... ) { // catch constructor exceptions
    // assumed: memory allocated but object not constructed
    free( ptr ); // release memory, object was not constructed?
    throw; // propagate whatever the exception was during construction?
  }
  throw std::bad_alloc(); // no memory allocated, throw bad_alloc?
}

编辑 - 请注意,为了简单起见,我忽略了对齐。

【问题讨论】:

  • 函数是否抛出或返回 nullptr 是一个设计决定。但是抛出一些错误并为其他人返回nullptr 的概念——你的第一个版本就是这样做的——会让你的代码的用户感到困惑/烦恼。

标签: c++11 exception-handling malloc unique-ptr placement-new


【解决方案1】:

假设两者都是,下面是正确处理这种情况还是有什么其他需要考虑的?

我觉得不错。

std::allocate_shared 使用自定义分配器分配的对象内存创建 shared_ptr。你所做的本质上是allocate_unique,它在标准 c++ 中不存在(也许还存在)。也许您可以创建使用 malloc/free 的自定义分配器,然后实现您自己的 make_unique(如果没有的话)和 allocate_unique

【讨论】:

    猜你喜欢
    • 2011-07-26
    • 2013-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    相关资源
    最近更新 更多