新增和删除在C ++ 14中仍然有用吗?

鉴于make_uniquemake_shared可用性,以及unique_ptrshared_ptr析构函数的自动删除,在C ++ 14中使用newdelete的情况是什么(除了支持遗留代码)呢?

在许多情况下,智能指针比原始指针更可取,但在C ++ 14中仍然有很多new / delete的用例。

如果您需要编写任何需要就地构build的内容,例如:

  • 一个内存池
  • 一个分配器
  • 一个标记的变体
  • 二进制消息到缓冲区

你将需要使用new位置,可能还需要delete 。 没办法。

对于您想要编写的某些容器,您可能需要使用原始指针进行存储。

即使对于标准的智能指针,如果你想使用自定义的删除器,你仍然需要new ,因为make_uniquemake_shared不允许这样做。

使用make_uniquemake_shared是比较常见的select,而不是对new raw调用。 但是,这不是强制性的。 假设你select遵循这个惯例,有几个地方可以使用new

首先,非定制的放置new (我会忽略“非定制”的一部分,只是把它称为放置new )是一个完全不同的纸牌游戏比标准(非放置) new 。 它在逻辑上与手动调用析构函数配对。 标准new都从免费商店获取资源,并在其中构build一个对象。 它与delete配对,销毁对象并将存储回收到免费商店。 从某种意义上说,标准的new调用放置在内部是new ,标准的delete在内部调用析构函数。

Placement new是您在某些存储上直接调用构造函数的方式,并且是高级生命周期pipe理代码所必需的。 如果您正在实现optionalalignment存储上的types安全union ,或智能指针(具有统一存储和非统一生命周期,如make_shared ),则将使用new位置。 然后在特定对象的生命周期结束时,直接调用它的析构函数。 就像非放置newdelete ,放置new和手动的析构函数调用是成对的。

自定义展示位置new是另一个使用new原因。 自定义布局new可用于从非全局池分配资源,或分配到跨进程共享内存页面,分配到video卡共享内存等等 – 以及其他目的。 如果你想写make_unique_from_custom来使用自定义位置new来分配它的内存,你必须使用new关键字。 自定义布局new可以像新的布局(因为它实际上并不获取资源,而是资源以某种方式传入),或者它可以像标准new一样行为(因为它获取资源,也许使用传入的参数) 。

如果自定义展示位置new引发,则调用自定义展示位置delete ,因此您可能需要编写该位置。 在C ++中,你不要调用自定义的位置delete ,它(C ++)会调用你(r重载)

最后, make_sharedmake_unique是不完整的函数,因为它们不支持自定义删除器。

如果你正在编写make_unique_with_deleter ,你仍然可以使用make_unique来分配数据,并且.release()它释放到你的unique-with-deleter中。 如果您的删除器想将其状态填充到指向缓冲区而不是unique_ptr或单独分配,则需要在此处使用new位置。

对于make_shared ,客户端代码无权访问“引用计数存根”创build代码。 据我可以告诉你不容易都有“对象和引用计数块的组合分配”和一个自定义删除器。

另外,只要weak_ptr持久化, make_shared就会导致对象自身的资源分配(存储):在某些情况下这可能是不可取的,所以你需要做一个shared_ptr<T>(new T(...))来避免这一点。

在less数情况下,如果您要独立于unique_ptr进行pipe理,可以调用make_unique ,然后调用指针.release() 。 这增加了RAII对资源的覆盖,意味着如果有例外或其他逻辑错误,则不太可能泄漏。


我上面指出,我不知道如何使用一个自定义的删除与共享指针,使用一个简单的分配块。 这是一个如何巧妙地做到这一点的草图:

 template<class T, class D> struct custom_delete { std::tuple< std::aligned_storage< sizeof(T), alignof(T) >, D, bool > data; bool bCreated() const { return std::get<2>(data); } void markAsCreated() { std::get<2>()=true; } D&& d()&& { return std::get<1>(std::move(data)); } void* buff() { return &std::get<0>(data); } T* t() { return static_cast<T*>(static_cast<void*>(buff())); } template<class...Ts> explicit custom_delete(Ts...&&ts):data( {},D(std::forward<Ts>(ts)...),false ){} custom_delete(custom_delete&&)=default; ~custom_delete() { if (bCreated()) std::move(*this).d()(t()); } }; template<class T, class D, class...Ts, class dD=std::decay_t<D>> std::shared_ptr<T> make_shared_with_deleter( D&& d, Ts&&... ts ) { auto internal = std::make_shared<custom_delete<T, dD>>(std::forward<D>(d)); if (!internal) return {}; T* r = new(internal->data.buff()) T(std::forward<Ts>(ts...)); internal->markAsCreated(); return { internal, r }; } 

我认为应该这样做。 我试图让无状态的删除者通过使用一个tuple来使用不起来的空间,但我可能已经搞砸了。

在一个库质量的解决scheme中,如果T::T(Ts...)noexcept ,我可以删除bCreated开销,因为在构造T之前不会有一个custom_delete被销毁的机会。

我能想到的唯一原因是偶尔您可能希望使用unique_ptrshared_ptr的自定义删除程序。 要使用自定义删除器,您需要直接创build智能指针,并传递new的结果。 即使这不是很频繁,但它确实出现在实践中。

除此之外, make_shared / make_unique似乎应该涵盖几乎所有的用途。

我会说newdelete的唯一原因是实现其他types的智能指针。

例如,该库仍然没有插入指针作为boost :: intrusive_ptr,这是遗憾的,因为它们在性能方面优于共享指针,正如Andrei Alexandrescu所指出的那样。