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

时间:2015-06-10 17:12:12

标签: c++ c++11 new-operator c++14 dynamic-memory-allocation

鉴于make_uniquemake_shared的可用性,以及unique_ptrshared_ptr析构函数的自动删除,除了支持遗留代码之外,还有哪些情况可供使用C ++ 14中的newdelete

4 个答案:

答案 0 :(得分:36)

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

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

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

您需要使用展示位置new,可能还需要使用delete。没办法。

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

对于标准智能指针

甚至,如果您想使用自newmake_unique之后的自定义删除工具,则仍需要make_shared允许这样做。

答案 1 :(得分:7)

使用make_uniquemake_shared而不是new的原始调用是一种相对常见的选择。但这不是强制性的。假设您选择遵循该约定,则有几个地方可以使用new

首先,非自定义展示位置new(我将忽略"非自定义"部分,并将其称为展示位置new)是一张完全不同的卡片游戏比标准(非展示位置)new。它在逻辑上与手动调用析构函数配对。标准new都从免费商店获取资源,并在其中构建对象。它与delete配对,它会破坏对象并将存储回收到免费商店。从某种意义上说,标准new会在内部调用new,标准delete会在内部调用析构函数。

放置new是您在某些存储上直接调用构造函数的方式,并且是高级生命周期管理代码所必需的。如果您正在实施optional,对齐存储上的类型安全union或智能指针(具有统一存储和非统一生命周期,如make_shared),您将使用展示位置{ {1}}。然后在特定对象的生命周期结束时,直接调用它的析构函数。与非展示位置newnew一样,展示位置delete和手动析构函数调用成对出现。

自定义展示位置new是使用new的另一个原因。自定义位置new可用于从非全局池分配资源 - 作用域分配,或分配到跨进程共享内存页,分配到视频卡共享内存等 - 以及其他用途。如果您要编写使用自定义展示位置新分配内存的new,则必须使用make_unique_from_custom关键字。自定义展示位置new可能就像放置新版本一样(因为它实际上并没有获取资源,而是以某种方式传递资源),或者它可以像标准new那样行事(因为它获取资源,可能使用传入的参数。)

如果自定义展示位置new抛出,则会调用自定义展示位置delete,因此您可能需要编写该展示位置new。在C ++中,您不能调用自定义展示位置delete(C ++)会调用(r overload)

最后,make_sharedmake_unique功能不完整,因为它们不支持自定义删除。

如果您正在撰写make_unique_with_deleter,您仍然可以使用make_unique分配数据,并.release()将其添加到您的独特删除小组中。如果您的删除者希望将其状态填充到指向缓冲区而不是unique_ptr或填充到单独的分配中,那么您需要在此处使用展示位置new

对于make_shared,客户端代码无法访问"引用计数存根"创作代码。据我所知,你不能轻易地同时具有对象和引用计数块的组合分配"和自定义删除器。

此外,只要make_shared s持续存在,weak_ptr就会导致对象本身的资源分配(存储)持续存在:在某些情况下,这可能不合适,所以你&# 39; d想要shared_ptr<T>(new T(...))来避免这种情况。

在您要拨打非展示位置new的少数情况下,如果您想要与{{1}分开管理,则可以调用make_unique,然后调用.release()指针}。这会增加您对资源的RAII覆盖率,并且意味着如果存在异常或其他逻辑错误,您就不太可能泄漏。

我在上面提到过,我不知道如何使用带有共享指针的自定义删除器,该共享指针可以轻松使用单个分配块。这是一个如何巧妙地做到这一点的草图:

unique_ptr

我认为应该这样做。我试图允许无状态删除者使用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 }; } 来不占空间,但我可能已经搞砸了。

在图书馆质量的解决方案中,如果tupleT::T(Ts...),我可以删除noexcept开销,因为bCreated没有机会在custom_delete构建之前被销毁。

答案 2 :(得分:3)

我能想到的唯一原因是,您可能希望在unique_ptrshared_ptr上使用自定义删除工具。要使用自定义删除器,您需要直接创建智能指针,传递new的结果。即使这种情况并不常见,但它确实在实践中出现。

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

答案 3 :(得分:1)

我想说newdelete的唯一原因是实现其他类型的智能指针。

例如,该库仍然没有像boost :: intrusive_ptr一样的侵入性指针,这是一个遗憾,因为它们因性能原因而优于共享指针,正如Andrei Alexandrescu指出的那样。