- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
出于测试目的,我需要在非零内存上构造一个对象。这可以通过以下方式完成:
{
struct Type { /* IRL not empty */};
std::array<unsigned char, sizeof(Type)> non_zero_memory;
non_zero_memory.fill(0xC5);
auto const& t = *new(non_zero_memory.data()) Type;
// t refers to a valid Type whose initialization has completed.
t.~Type();
}
由于这很乏味并且多次进行,我想提供一个函数,返回指向这样一个 Type
实例的智能指针。我想出了以下内容,但我担心未定义的行为潜伏在某个地方。
以下程序是否定义明确?特别是,一个 std::byte[]
已经被分配,但是一个等量大小的 Type
被释放是一个问题吗?
#include <cstddef>
#include <memory>
#include <algorithm>
auto non_zero_memory(std::size_t size)
{
constexpr std::byte non_zero = static_cast<std::byte>(0xC5);
auto memory = std::make_unique<std::byte[]>(size);
std::fill(memory.get(), memory.get()+size, non_zero);
return memory;
}
template <class T>
auto on_non_zero_memory()
{
auto memory = non_zero_memory(sizeof(T));
return std::shared_ptr<T>(new (memory.release()) T());
}
int main()
{
struct Type { unsigned value = 0; ~Type() {} }; // could be something else
auto t = on_non_zero_memory<Type>();
return t->value;
}
最佳答案
这个程序没有很好的定义。
规则是,如果一个类型有 trivial destructor (见 this ),你不需要调用它。所以,这个:
return std::shared_ptr<T>(new (memory.release()) T());
几乎是正确的。它省略了 sizeof(T)
的析构函数std::byte
s,这很好,构造一个新的 T
在内存中,这很好,然后当 shared_ptr
准备删除,它调用delete this->get();
,这是错误的。首先解构 T
,但随后它会释放 T
而不是 std::byte[]
,这可能(未定义)不起作用。
C++ 标准 §8.5.2.4p8 [expr.new]
A new-expression may obtain storage for the object by calling an allocation function. [...] If the allocated type is an array type, the allocation function's name is
operator new[]
.
(所有这些“可能”是因为允许实现合并相邻的新表达式,并且只为其中一个调用 operator new[]
,但情况并非如此,因为 new
只发生一次(在 make_unique
中) ))
以及同一部分的第 11 部分:
When a new-expression calls an allocation function and that allocation has not been extended, the new-expression passes the amount of space requested to the allocation function as the first argument of type
std::size_t
. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array. For arrays ofchar
,unsigned char
, andstd::byte
, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement (6.6.5) of any object type whose size is no greater than the size of the array being created. [Note: Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating character arrays into which objects of other types will later be placed. — end note ]
如果您阅读 §21.6.2 [new.delete.array],您会看到默认的 operator new[]
和 operator delete[]
做与 operator new
完全相同的事情和 operator delete
,问题是我们不知道传递给它的大小,它可能比delete ((T*) object)
调用(存储大小)。
看看删除表达式的作用:
§8.5.2.5p8 [expr.delete]
[...] delete-expression will invoke the destructor (if any) for [...] the elements of the array being deleted
p7.1
If the allocation call for the new-expression for the object to be deleted was not omitted [...], the delete-expression shall call a deallocation function (6.6.4.4.2). The value returned from the allocation call of the new-expression shall be passed as the first argument to the deallocation function.
自从 std::byte
没有析构函数,我们可以安全地调用 delete[]
,因为它除了调用 deallocate 函数(operator delete[]
)之外不会做任何事情。我们只需将其重新解释为 std::byte*
, 我们将返回 new[]
返回。
另外一个问题是如果T
的构造函数存在内存泄漏抛出。一个简单的解决方法是放置 new
而内存仍归 std::unique_ptr
所有, 所以即使它抛出它也会调用 delete[]
正确。
T* ptr = new (memory.get()) T();
memory.release();
return std::shared_ptr<T>(ptr, [](T* ptr) {
ptr->~T();
delete[] reinterpret_cast<std::byte*>(ptr);
});
第一个展示位置new
结束 sizeof(T)
的生命周期std::byte
s 并开始一个新的 T
的生命周期对象在同一地址,根据 §6.6.3p5 [basic.life]
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. [...]
那么当它被删除时,T
的生命周期以显式调用析构函数结束,然后根据上述,delete-expression 释放存储空间。
这就引出了以下问题:
如果存储类不是 std::byte
怎么办? ,并且不是可以轻易破坏的吗?例如,我们使用非平凡的 union 作为存储。
调用delete[] reinterpret_cast<T*>(ptr)
会在不是对象的东西上调用析构函数。这显然是未定义的行为,并且符合 §6.6.3p6 [basic.life]
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [...], any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways. [...] The program has undefined behavior if: the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a delete-expression
所以要像上面那样使用它,我们必须构造它只是为了再次破坏它。
默认构造函数可能工作正常。通常的语义是“创建一个可以被破坏的对象”,这正是我们想要的。使用 std::uninitialized_default_construct_n
将它们全部构建然后立即销毁它们:
// Assuming we called `new StorageClass[n]` to allocate
ptr->~T();
auto* as_storage = reinterpret_cast<StorageClass*>(ptr);
std::uninitialized_default_construct_n(as_storage, n);
delete[] as_storage;
我们也可以调用operator new
和 operator delete
我们自己:
static void byte_deleter(std::byte* ptr) {
return ::operator delete(reinterpret_cast<void*>(ptr));
}
auto non_zero_memory(std::size_t size)
{
constexpr std::byte non_zero = static_cast<std::byte>(0xC5);
auto memory = std::unique_ptr<std::byte, void(*)(std::byte*)>(
reinterpret_cast<std::byte*>(::operator new(size)),
&::byte_deleter
);
std::fill(memory.get(), memory.get()+size, non_zero);
return memory;
}
template <class T>
auto on_non_zero_memory()
{
auto memory = non_zero_memory(sizeof(T));
T* ptr = new (memory.get()) T();
memory.release();
return std::shared_ptr<T>(ptr, [](T* ptr) {
ptr->~T();
::operator delete(ptr, sizeof(T));
// ^~~~~~~~~ optional
});
}
但这看起来很像 std::malloc
和 std::free
.
第三种解决方案可能是使用 std::aligned_storage
作为给 new
的类型, 并让删除器与 std::byte
一样工作因为对齐的存储是一个微不足道的聚合。
关于c++ - 可以在智能指针管理的内存上创建新的位置吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54095233/
我遇到了一个奇怪的问题。我的应用程序的 Release 版本似乎运行良好,但最近当我切换到 Debug 版本时,我在启动时立即遇到访问冲突。当释放分配的内存块时,就会发生访问冲突。所有这些都发生在静态
我在 C# 中偶然发现了这种奇怪的语法形式,并试图弄清楚它的含义以及如何使用它。网络上似乎没有关于此的任何文档。 object data = new { var1 = someValue, var2
我正在尝试使用浏览器的内置类型 CSSStyleDeclaration 以编程方式传递和修改样式(由于 .cssText 属性,这很方便)。 但是,new CSSStyleDeclaration()
我有现成的代码: internal bool firstAsSymbol(out Symbol s) { return (s = first as Symbol) !=
在新的 Eclipse 版本 2022-03 中,一些(但不是全部)java 项目在 Project Explorer View 中的外观发生了变化。尽管 Package Presentation 设
我正在尝试使用 FormData 通过获取 API 在 POST 请求中发送用户输入的数据。问题是,当我用我创建的表单创建一个新的 FormData 对象时,它一直在创建一个空对象——没有条目/键/值
我有一个用一些 intel-intrinsincs 编写的 C 代码。在我先用 avx 然后用 ssse3 标志编译后,我得到了两个完全不同的汇编代码。例如: AVX: vpunpckhbw %xm
最近,discord 为您自己的应用程序添加了对斜杠命令的支持。我通读了它的文档,并尝试搜索一些视频(但是该功能刚刚出现),但我不明白我实际上需要做什么才能使其正常工作。我正在使用 WebStorm(
我想使用 JRI 从 Java 调用 R。 我在 eclipse 下在主类中运行它: Rengine c = new Rengine(new String[] { "--vanilla" },
我正在使用新的 Place Autocomplete那是来自新的静态Google Places SDK 客户端库 (here)。所以它真的很容易使用,我刚得到this tutorial它按预期工作。
我刚刚更新到 flutter 版本 1.25.0-5.0.pre.92,我的代码中出现了很多与空安全相关的错误,这些错误以前运行良好。我没有以任何方式选择空安全,我所做的只是运行 flutter 升级
我已经使用 React Native 有一段时间了,但我想我会在网络上试用 React。所以我遵循了这个指南:https://reactjs.org/docs/create-a-new-react-a
周六早上在这里。尝试学习新的 Scala 编译器 dotty。 安装在我的 Mac 上使用 brew install lampepfl/brew/dotty 安装成功。我有版本 dotr -versi
我使用了谷歌地方的新依赖。单击自动完成 View 时应用程序崩溃。错误如下。, java.lang.NullPointerException: Place Fields must be set.
我关注了这个博客-> https://medium.com/@teyou21/training-your-object-detection-model-on-tensorflow-part-2-e9e
在哪里可以找到用于在此架构上进行组装的新寄存器的名称? 我指的是 X86 中的寄存器,如 EAX、ESP、EBX 等。但我希望它们是 64 位的。 我认为它们与我反汇编 C 代码时不同,我得到的是 r
新的服务总线库 Azure.Messaging.ServiceBus 使用 ServiceBusReceivedMessage 来接收消息 https://learn.microsoft.com/en
需要使用实时流媒体 channel 的实时编码类型在新的 Azure 门户中配置广告插入和石板图像。请帮忙解决这个问题,因为我找不到该功能。 最佳答案 此处描述了 Azure 媒体服务的广告插入选项
我正在使用新的 GitHub 操作,下面的工作流程的想法是在打开或同步 pr 时运行,它应该首先检查并安装依赖项,然后运行一些 yarn 脚本 name: PR to Master on: pul
我听说 DMD 2.058 中将有一个用于匿名函数的新语法,但我找不到任何相关信息。新语法是什么?旧语法是否会被弃用? 最佳答案 我相信它就像 C#'s . 以下内容是等效的: delegate(i,
我是一名优秀的程序员,十分优秀!