- 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/
我正在尝试获取从过去的 startposition/location 到当前移动的 currentposition/location 的距离(以米为单位)。 我确实有工作正常的currentposit
所以我有一堆绝对覆盖的 div。用户通过在叠加层上拖动来创建方形 div。如果您要创建一个 div,然后放大和缩小,div 会保持在同一位置,因为它对叠加层是绝对的,如前所述。 然而问题就出在这里。您
我想找到 View 在显示屏幕上的位置。 为此,我使用了 view.getLeft() 、view.getBottom() 、view.getRight() 等方法> , view.getTop()。
我有一个看起来像这样的 View 层次结构(基于其他答案和 Apple 的使用 UIScrollView 的高级 AutoLayout 指南): ScrollView 所需的2 个步骤是: 为 Scr
所以我有一个名为 MARKS 的表,我有这些列 STUDENT_ID, CLASSFORM_NAME, ACADEMIC_YEAR, TERM, SUBJECT_NAME, TOTAL_MARKS
我有一个问题我无法理解,请帮助: 我开发了带有图像的 html 页面,并使用 jQuery UI 帮助使它们可拖动,我将这些图像位置设置为相对位置并给出了左侧和顶部像素,这是页面的链接 http://
我正在尝试创建一个 CSS 动画,它在 sprite 表中循环播放 16 个图像,给人一种幽灵“漂浮”的错觉。动画通过在 background-position 位置之间移动以显示不同状态的幽灵来实现
我正在创建这个网站的 WebView https://nearxt.com/打开时询问位置但是当我使用此链接在 flutter 中创建 webview 时那么它就无法定位我还在应用程序中定义了位置,但
我正在以编程方式创建一个需要跨越 2 个屏幕的窗口。正在创建的窗口的大小是正确的,但窗口大约从第一个屏幕的一半开始。我可以将它拖回第一个屏幕的开头,NSWindow 非常适合。 我只需要知道在窗口的起
位置“/”的匹配叶路由没有元素。这意味着默认情况下它将呈现一个空值,从而导致一个“空”页面 //App.js File import { BrowserRouter as Router, Routes
我有一个运行 Ubuntu 和 Apache 的 VPS 例如,假设地址是:5.5.5.5 在 VPS 上,我有一个名为 eggdrop 的用户(除了我的 root 用户)。 用户 eggdrop 有
我有一个 JLabel与 ImageIcon ,我使用 setIcon() JLabel中的函数. ImageIcon然后上来,坐在我的JLabel 的文字左侧.是否有可能拥有 ImageIcon在文
我的图中有节点,它们的 xlabels 位于它们的左上方。我怎样才能改变这个位置?我希望 xlabels 正好位于节点本身的旁边。 最佳答案 xlp是你想要的属性,但它没有做任何事情。 你不能改变位置
我对基本的 VIM 功能有疑问:(我尝试谷歌搜索但找不到答案) 如何列出所有自定义功能。(我做了 :function 并且不能找到我的自定义函数) 如何获得自定义函数列表中的函数(或它们的存储位置)。
我是 PHP 的新手,虽然我一直在搜索,但我不知道该怎么做。 我知道可以使用 Location("some page") 进行重定向。我还读到,只要没有向用户显示任何内容,它就可以工作。 我想做的是:
如果在 jgrowl.css 中位置更改为“center”,我如何将其覆盖为默认值,即“top-right” $.jGrowl(data, { header: 'data', an
我需要根据用户是否滑动屏幕顶部、屏幕中间或屏幕底部来触发不同的事件。我正在尝试找出最好/最简单的方法来做到这一点,因为我很确定没有办法从 UISwipeGestureRecognizer 获取位置。
我需要枚举用delphi编写的外部应用程序中使用的类 ,因此我需要访问VMT表以获取该信息,但是我找不到任何有关如何在exe(由delphi生成)文件中找到VMT(虚拟方法表)的位置(地址)的文档。
在 D2010 (unicode) 中是否有像 Pos 这样不区分大小写的类似函数? 我知道我可以使用 Pos(AnsiUpperCase(FindString), AnsiUpperCase(Sou
我正在尝试为我的reveal.js 演示文稿制作一个标题,该标题会粘贴在屏幕顶部。标题中的内容在每张幻灯片的基础上都是动态的,因此我必须将标记放在 section 标记中。 显然,如果标记在 sect
我是一名优秀的程序员,十分优秀!