【问题标题】:Smart replacement for 'new[]' to make_unique将 'new[]' 智能替换为 make_unique
【发布时间】:2016-06-07 11:44:35
【问题描述】:

我正在替换以下语句:

auto pszOutBuffer = new char[dwSize];

ReadDataFromHttp(pszOutBuffer, dwSize);
if(dwSize>100)
    ParseHttpData(pszOutBuffer);
    ...
delete []pszOutBuffer;

到:

auto OutBufferPtr = make_unique<char[]>(dwSize);
auto pszOutBuffer = OutBufferPtr.get();

ReadDataFromHttp(pszOutBuffer, dwSize);
if(dwSize>100)
    ParseHttpData(pszOutBuffer);
...

这样我就可以利用智能指针unique_ptr。我想保留变量 pszOutBuffer,这样 Git 提交中的更改就会更少,并且不需要重复 unique_ptr.get()

为了使这个简单,并且阅读起来不那么冗长;我想过编写一个宏 MAKE_UNIQUE_PTR(type,size),它将是一个语句(不是两个如上所示)。但是这样的宏将无法拥有unqiue_ptrpszOutBuffer,如:

auto pszOutBuffer = MAKE_UNIQUE_PTR(char, dwSize);

我可能会考虑写一个函数模板,但是……如何在函数(MAKE_UNIQUE_PTR)返回后保持unique_ptr

编辑:

有了这个假设的宏/函数,代码就是:

auto pszOutBuffer = MAKE_UNIQUE_PTR(char, dwSize);

ReadDataFromHttp(pszOutBuffer, dwSize);
if(dwSize>100)
     ParseHttpData(pszOutBuffer);
...

具有这些优势:

  • unique_ptr 仍然控制着缓冲区的生命周期。
  • 原始样式指针仍然存在,而不会将它们更改为 unique_ptr::get() 调用。
  • 因此可以安全地从多个代码路径中删除delete[] 调用。

请注意,第一个代码是我手头的代码。没有宏,没有unique_ptr——只有具有newdelete 的遗留代码。是的……那些匈牙利符号变量。

【问题讨论】:

  • 不确定是否离题...为什么dwSize + 1?您的 memset 超出了pszOutBuffer 的范围,这将导致 UB。
  • 坚持你所拥有的——任何人都容易理解。并非一切都是代码高尔夫。
  • @Ajay 不,只是写出auto OutBufferPtr = make_unique&lt;char[]&gt;(dwSize);
  • 每当您想到“动态数组”时,您接下来应该想到std::vector。所以不要打扰指针,不管聪明与否,改用std::vector
  • 这是一个意见......但我认为有一些优先事项混淆了。你想为了更简单的 git 历史而牺牲可读性和可维护性......你真的确定吗? 您正在为auto 变量使用匈牙利符号,这是一个很大的“什么鬼”,除此之外,您还可以将它与宏结合使用。想想更典型的用例,git 历史浏览与实际代码阅读。 PS。人们会选择“XY”,因为这似乎是一个实际问题,并且您会努力寻找合适的解决方案,因此他们关心质量。

标签: c++ c++11 c++14 unique-ptr


【解决方案1】:

很遗憾,不支持std::make_unique&lt;T[N]&gt;(),原因您可以在original proposal 中看到。但是,没有什么能阻止您为数组(例如,make_unique_array)制作自己的 make_unique,如下所示:

template<typename T>
std::enable_if_t<std::is_array<T>::value, std::unique_ptr<T>>
make_unique_array(std::size_t const n) {
  using RT = std::remove_extent_t<T>;
  return std::unique_ptr<T>(new RT[n]);
} 

【讨论】:

  • std::remove_extent 加一个!我正在为使用数组智能指针的自定义分配器实现寻找类似的东西!谢谢!
  • 谢谢。这是一个很好的答案,它解决了我在(自己的)已删除问题中提出的问题:stackoverflow.com/questions/37676450/…
【解决方案2】:

由于您已经在使用宏,您可以编写一个同时创建两者的宏。它很丑,但它的工作。

#define SmartMacro(pszOutBuffer, dwSize) \
                     auto pszOutBuffer##ptr = make_unique<char[]>(dwSize); \
                     auto pszOutBuffer = pszOutBuffer##ptr.get(); \
                     memset(pszOutBuffer, 0, dwSize);

// Usage
SmartMacro(buffer, 10);
// 'buffer' is the raw pointer
// 'bufferptr' is the unique pointer

【讨论】:

  • 谢谢。但我不想要memset,而是一个原始风格的、可重复使用的pszOutBuffermemset 只是为了说明。
  • @thorsan 不,它应该创建一个后缀“ptr”的名称。
  • @Ajay 这将按照你给它的名字创建智能指针和原始指针。
  • 但是你没有使用 OutBufferPtr.get,这应该是你拥有 unique_ptr 的名字吗?
  • name 还没有定义,它不叫 buffer 和 bufferptr
【解决方案3】:
template<class T, class D>
struct smart_unique:std::unique_ptr<T,D> {
  using std::unique_ptr<T,D>::unique_ptr;
  operator T*()const{return this->get();}
  operator T const*()const{return this->get();}
};
template<class T, class D>
struct make_smart_unique_t {
  template<class...Ts>
  smart_unique<T, D> operator()(Ts&&...ts)const{
    return smart_unique<T,D>( new T(std::forward<Ts>(ts)...); );
  }
  template<class T0, class...Ts>
  smart_unique<T, D> operator()(std::initializer_list<T0> il, Ts&&...ts)const{
    return smart_unique<T,D>( new T(il, std::forward<Ts>(ts)...); );
  }
};
template<class T, class D>
struct make_smart_unique_t<T[], D> {
  smart_unique<T[], D> operator[](std::size_t N)const{
    return smart_unique<T[],D>( new T[N]; );
  }
  template<class...Ts>
  smart_unique<T[], D> operator()(Ts&&...ts)const{
    return smart_unique<T[],D>( new T[sizeof...(Ts)]{std::forward<Ts>(ts)...}; );
  }
};
template<class T, class D=std::default_delete<T>>
constexpr make_smart_unique_t<T,D> make_smart_unique{};

这应该支持:

auto pszOutBuffer = make_smart_unique<char[]>[dwSize];

ReadDataFromHttp(pszOutBuffer, dwSize);
if(dwSize>100)
  ParseHttpData(pszOutBuffer);

还有:

auto pszDataBuffer = make_smart_unique<int[]>(1,2,3,4,5);

auto pszDataBuffer = make_smart_unique<int>();

不需要宏魔法。

这里的设计很简单:make_smart_unique&lt;scalar&gt; 类似于make_unique,但它返回一个smart_unique(隐式转换为T*:要小心!)

make_smart_unique&lt;Array[]&gt; 有两种不同的调用方式。 [N] 创建一个传入大小的数组:(args...) 创建一个元素数量大小的数组,并从每个 arg 构造每个数组。

【讨论】:

    猜你喜欢
    • 2019-10-09
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 2019-04-17
    • 2018-06-04
    • 1970-01-01
    • 2011-03-04
    相关资源
    最近更新 更多