除了支持旧代码外,在C++14中何时使用new
和delete
?鉴于现在可以使用make_unique
和make_shared
,以及由unique_ptr
和shared_ptr
析构函数自动删除内存的能力。
除了支持旧代码外,在C++14中何时使用new
和delete
?鉴于现在可以使用make_unique
和make_shared
,以及由unique_ptr
和shared_ptr
析构函数自动删除内存的能力。
new
/delete
的用例。如果需要编写任何需要就地构建的内容,例如:内存池、分配器、标记变量、二进制消息到缓冲区,那么您将需要使用放置new
,可能还需要使用delete
。对于某些要编写的容器,您可能希望使用原始指针进行存储。即使对于标准智能指针,如果要使用自定义删除器,您仍将需要new
,因为make_unique
和make_shared
不允许这样做。new
关键字。 - sbabbistd::allocator
可以干净而通用地 allocate
/deallocate
和 construct
/destroy
元素。 - edmzplace_shared
?放置new
是一种语法,可以直接在内存块上调用构造函数。非放置new
首先是获取空间,然后进行构造。unique_ptr
和shared_ptr
是关于管理生命周期的。make_unique
和make_shared
是在智能指针中获取资源、构造和管理它们的组合。由于放置new不涉及资源(只涉及构造),因此与资源管理正交。 - Yakk - Adam Nevraumontnew
和非放置 new
一样,需要手动调用析构函数,以配成一对。从某种意义上说,放置 new
只是与非放置 new
略微相关:非放置 new
实际上调用了 new
,而 delete
调用了析构函数。 - Yakk - Adam Nevraumontmake_unique
和make_shared
相对于直接调用new
来说是一种比较常见的选择,但这并不是强制性的。假设你选择遵循这个约定,那么有几个地方需要使用new
。new
(我将忽略“非自定义”的部分,只称之为定位new
)与标准(非定位)new
是完全不同的。它逻辑上与手动调用析构函数配对。标准new
既从空闲存储中获取资源,又在其中构造一个对象。它与delete
配对,后者销毁对象并将存储回收到空闲存储区。在某种意义上,标准new
在内部调用定位new
,而标准delete
在内部调用析构函数。new
是一种在某些存储空间上直接调用构造函数的方法,它是高级生命周期管理代码所必需的。如果你正在实现optional
、一个类型安全的union
或一个智能指针(具有统一存储和非统一生命周期,如make_shared
),你将使用定位new
。然后在特定对象的生命周期结束时,直接调用它的析构函数。与非定位new
和delete
一样,定位new
和手动析构函数调用是成对出现的。new
是使用new
的另一个原因。自定义定位new
可用于从非全局池(作用域分配或分配到跨进程共享内存页、分配到视频卡共享内存等)中分配资源和其他目的。如果你想编写make_unique_from_custom
,以使用自定义定位new
分配内存,那么你必须使用new
关键字。自定义定位new
可以像定位new
一样操作(即实际上不会获取资源,而是某种方式传递资源),也可以像标准new
一样操作(即获取资源,可能使用传递的参数)。new
抛出异常,则会调用自定义定位delete
,因此你可能需要编写它。在C++中,不是你调用自定义定位delete
,而是它调用你的重载函数。make_shared
和make_unique
是不完整的函数,因为它们不支持自定义删除器。make_unique_with_deleter
,你仍然可以使用 make_unique
来分配数据,并将其 .release()
到你的 unique-with-deleter 中进行处理。如果你的删除器想要将其状态存储到指向的缓冲区中而不是存储到 unique_ptr
或单独分配的内存中,则需要在此处使用定位 new
。make_shared
,客户端代码无法访问“引用计数桩”创建代码。据我所知,您不能轻松地同时拥有“对象和引用计数块的组合分配”和自定义删除器。make_shared
导致对象本身的资源分配(存储)持续存在,只要 weak_ptr
持续存在:在某些情况下,这可能不是理想的,因此您需要执行 shared_ptr<T>(new T(...))
以避免出现这种情况。new
,您可以调用 make_unique
,然后 .release()
指针如果你想从那个 unique_ptr
中单独管理。这增加了对资源的 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
让无状态的删除器不使用 up 空间,但是可能我搞砸了。T::T(Ts...)
是 noexcept
的话,我可以去掉 bCreated
的开销,因为在构造 T
之前不需要销毁 custom_delete
。allocate_shared
еҮҪж•°дёӯзҡ„вҖңеҲҶй…ҚеҷЁзҡ„еүҜжң¬иў«еӯҳеӮЁеңЁжҺ§еҲ¶еқ—дёӯпјҢд»ҘдҫҝеңЁе…ұдә«еј•з”Ёи®Ўж•°е’Ңејұеј•з”Ёи®Ўж•°йғҪиҫҫеҲ°йӣ¶ж—¶еҸҜд»ҘдҪҝз”Ёе®ғжқҘйҮҠж”ҫеҶ…еӯҳвҖқпјҢдҪҶжҲ‘еңЁж ҮеҮҶдёӯжүҫдёҚеҲ°иҝҷж ·зҡ„дҝқиҜҒгҖӮ - dypshared_ptr
)。 - dypconstruct
和destroy
函数。对于某些分配器,调用construct
而不是placement-new(例如scoped_allocator_adapter
)非常重要。虽然我不知道任何关于destruct
的真实用例。我希望支持分配器的shared_ptr
在构建和销毁时都使用分配器接口。但标准在这里非常模糊,可能存在缺陷。我知道在实现方面存在与std::vector
和此接口的使用相关的问题,就我所看到的而言,在libc++中也存在类似的问题。 - dypunique_ptr
或shared_ptr
中使用自定义删除器。为了使用自定义删除器,你需要直接创建智能指针,并传入new
的结果。即使这种情况并不经常出现,但实际上确实会有这种情况出现。make_shared
/make_unique
应该涵盖几乎所有用途。shared_ptr
而不直接调用非放置new
?对于unique_ptr
,甚至更容易:创建std::unique_ptr<T, D>(make_unique<T>(...).release(), deleter)
--没有调用new
!(如果deleter
构造函数抛出,则无效)。 - Yakk - Adam Nevraumontnew
和delete
。
new
和/或delete
,仍然需要使用原始指针。 - Galik