- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
在下面的代码中,我一直在仔细遵循标准中关于对象生命周期的词语(加上 P0137 的措辞)。
请注意,根据 P0137,所有内存分配都是通过 unsigned char 类型的适当对齐存储进行的。
另请注意,Foo
是一个 POD,具有一个简单的构造函数。
一个。如果我对标准理解有误,这里有任何UB,请指出(或者确认没有UB)
B. A、B、C、D、E、F 处的初始化是否严格是必要的,因为构造是微不足道的,并且不执行实际的初始化。如果是这样,请指出标准的哪一部分在这方面与 [object.lifetime] 矛盾或澄清。
#include <memory>
// a POD with trivial constructor
struct Foo
{
int x;
};
struct destroy1
{
void operator()(Foo* p)
{
// RAII to guarantee correct destruction order
auto memory = std::unique_ptr<unsigned char[]>(reinterpret_cast<unsigned char*>(p));
p->~Foo(); // A
}
};
std::unique_ptr<Foo, destroy1> create1()
{
// RAII to guarantee correct exception handling
auto p = std::make_unique<unsigned char[]>(sizeof(Foo));
auto pCandidate = reinterpret_cast<Foo*>(p.get());
new (pCandidate) Foo(); // B
return std::unique_ptr<Foo, destroy1>(reinterpret_cast<Foo*>(p.release()),
destroy1());
}
struct call_free
{
void operator()(void *p) const { std::free(p); }
};
using malloc_ptr = std::unique_ptr<unsigned char, call_free>;
struct destroy2
{
void operator()(Foo *pfoo) const {
// RAII to guarantee correct destruction order
auto memory = malloc_ptr(reinterpret_cast<unsigned char*>(pfoo));
pfoo->~Foo(); // C
}
};
std::unique_ptr<Foo, destroy2> create2()
{
// RAII to guarantee correct exception handling
auto p = malloc_ptr(reinterpret_cast<unsigned char*>(std::malloc(sizeof(Foo))));
auto pCandidate = reinterpret_cast<Foo*>(p.get());
new (pCandidate) Foo(); // D
return std::unique_ptr<Foo, destroy2>(reinterpret_cast<Foo*>(p.release()),
destroy2());
}
struct nodelete {
void operator()(Foo * p) {
p->~Foo(); // E
}
};
std::shared_ptr<Foo> provide()
{
alignas(Foo) static unsigned char storage[sizeof(Foo)];
auto make = [] {
auto p = reinterpret_cast<Foo*>(storage);
new (p) Foo (); // F
return std::shared_ptr<Foo>(p, nodelete());
};
static std::shared_ptr<Foo> pCandidate = make();
return pCandidate;
}
int main()
{
auto foo1 = create1();
auto foo2 = create2();
auto foo3 = provide();
foo1->x = 1;
foo2->x = 2;
foo3->x = 3;
}
最佳答案
create1
std::unique_ptr<Foo, destroy1>(reinterpret_cast<Foo*>(p.release()), destroy1());
这不起作用,因为您使用了错误的指针。
p.release()
认为它指向一个 unsigned char[]
。但是,这不是您要指向的对象。您要指向的是此数组中的对象,即您创建的 Foo
。
所以你现在受制于 [basic.life]/8。其要点是,如果它们属于同一类型,则只能将前一个指针用作指向新对象的指针。他们不是你的情况。
现在,我可以告诉您清理
指针,但更合理的处理方式是只存储由 placement-new 调用返回的指针:
auto p = std::make_unique<unsigned char[]>(sizeof(Foo));
auto ret = std::unique_ptr<Foo, destroy1>(new(p.get()) Foo(), destroy1());
p.release();
return ret;
那个指针永远是正确的。
您对 placement-new 的使用不是可选的。 [intro.object]/1 告诉我们:
An object is created by a definition (3.1), by a new-expression (5.3.4), when implicitly changing the active member of a union (9.3), or when a temporary object is created (4.4, 12.2).
当您分配 unsigned char[]
时,这就是您在该存储中创建的对象。您不能仅仅因为 Foo
是一个聚合就假装它是一个 Foo
。 [intro.object]/1 不允许这样做。您必须通过上面列出的机制之一显式创建该对象。由于您不能使用定义、union
成员激活或具有任意内存缓冲区的临时对象来从现有存储创建对象,因此创建对象的唯一方法是 new 表达式。
具体来说,placement-new。
至于delete1
,您确实需要一个自定义删除器,因为默认删除器会在Foo
指针上调用delete
。您的代码如下:
auto memory = std::unique_ptr<unsigned char[]>(reinterpret_cast<unsigned char*>(p));
p->~Foo();
由于 [intro.object]/3-4,
unsigned char[]
有一些特殊的逻辑,当对象在其存储中分配时它的行为方式。如果对象完全覆盖 unsigned char[]
的存储,那么它的功能就好像对象是在数组中分配的一样。这意味着 unsigned char[]
在技术上仍然存在;它没有破坏字节数组。
因此,您仍然可以删除字节数组,您的代码会这样做。
create2
这也是错误的,因为进一步违反了 [basic.life]/8。固定版本与上述类似:
auto p = malloc_ptr(reinterpret_cast<unsigned char*>(std::malloc(sizeof(Foo))));
auto ret std::unique_ptr<Foo, destroy2>(new(p.get()) Foo(), destroy2());
p.release();
return ret;
与 new 表达式不同,malloc
从不通过 [intro.object]/1 创建对象;它只获取存储空间。因此,再次需要 placement-new。
同样,free
只是释放内存;它不处理对象。所以你的 delete2
基本上没问题(虽然 malloc_ptr
的使用会造成不必要的混淆)。
提供
这与您的其他示例具有相同的 [basic.life]/8 问题:
alignas(Foo) static unsigned char storage[sizeof(Foo)];
static auto pCandidate = std::shared_ptr<Foo>(new(storage) Foo(), nodelete());
return pCandidate;
但除此之外,它很好(只要你不在其他地方破坏它)。为什么?这很复杂。
[basic.start.term]/1 告诉我们静态对象的销毁顺序与其初始化相反。 [stmt.decl]/4 告诉我们, block 范围的静态对象按照它们在函数中遇到的顺序进行初始化。
因此,我们知道pCandidate
在存储
之前会被销毁。只要您不在静态变量中保留该 shared_ptr
的拷贝,或者在终止前未能销毁/重置所有此类共享对象,您应该没问题。
综上所述,使用 unsigned char
block 实际上是 C++11 之前的做法。我们有std::aligned_storage
和 std::aligned_union
现在。使用它们。
关于c++ - 澄清 P0137 的细节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40930475/
前言 本文主要介绍了关于MySQL主键为0与主键自排约束的关系,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。 开始不设置主键表的设计如下: 如果id的位置有好几个0
我已经阅读了一些关于将消息从一个线程冒泡到所有其他线程以正常退出的正确方法的来源(每个线程都执行它自己的退出例程)。其中,我喜欢全局原子 bool 值的想法,它可以从任何线程进行标记,所有其他线程检查
本文深入探讨Go语言中的流程控制语法,包括基本的 if-else 条件分支、 for 循环、 switch-case 多条件分支,以及与特定数据类型相关的流程控制,如 for-r
我是 MVC 和 XCode 的新手,在将我对 MVC 的概念理解转化为设计和实现具体类时遇到了困难。我希望就如何构建 Controller 和 View 以获得预期的 UI 获得一些建议。这是针对
如果我尝试在 View 中打开 DeatilFragement,我的应用程序崩溃并收到以下错误: Caused by: java.lang.IllegalStateException: Require
我正在尝试构建我的 iOS 应用程序的界面。一遍又一遍地开始新项目我仍然遇到细节 View 控件的问题(见图)。 在这里我得到了截图: 详细 View 显示当用户触摸 UITableView 行时。您
我在与我正在处理的项目的类(class)中遇到问题。该类是一个接受标签和值的 GUI 组件。这里的想法是,用户可以指定一个标签,然后从任何地方链接一个值(更具体地说,该值的 ToString 方法),
嗯.. 我在我的应用程序中设置了表格 View - 详细 View 。 主视图使用常规代码将数据传递给详细 View - (void)tableView:(UITableView *)tableVie
我有 celery 任务,队列中有 100 个输入数据,需要使用 5 个 worker 来执行。 如何获取哪个工作人员正在执行哪个输入? 每个 worker 执行了多少输入及其状态? 如果任何任务失败
我有一个 .net github 项目,它基本上是一个 Web API 的包装器。在测试项目中,我使用 API key 调用 API。我需要将此 key 保密,如何在 Visual Studio 项目
我遇到一个问题,从 Ag-Grid 导出网格只会导出主网格的详细信息,而不会导出子网格。这是一个显示问题的 plunkr: https://next.plnkr.co/edit/jVcvWDJ1NKP
我在详细 View 中有一个不会消失的额外空间。该 View 来自 NavigationLink,但我已经尝试过使用或不使用 NavigationView。我试图包装它用 NavigationView
几天来,我一直在关注猫效应和 IO。我觉得我对这种效果有一些误解,或者只是我错过了它的重点。 首先——如果IO可以替代Scala的Future,我们如何创建异步IO任务?使用 IO.shift ?使用
如何将标高添加到主视图/详细 View 的详细信息 Pane 中,以在其下方提供阴影,同时定位为部分覆盖工具栏(如下面的底部图片)?我尝试使用 android:elevation="4dp" 但这对我
我试图在我的 UISplitViewController 的细节 View 上设置一个阴影,我希望在 iOS 6 中的主 View 上可见。 在我的细节 View Controller 中: sel
我正在阅读 std::basic_string::reserve(size_type res_arg=0) 上的标准.它是这样说的: void reserve(size_type res_arg=0)
Boost 文档说 Starting with Boost release 1.53, shared_ptr can be used to hold a pointer to a dynamicall
我用 OpenGL 编写了一个简单的 24 位位图加载器。我打开一个位图文件并读取它的像素,然后从中创建一个 RGB 像素数据数组,然后将其传递给 glDrawPixels()。 问题:我需要使图像的
for x in ...循环 就是把每个元素代入变量x,然后执行缩进块的语句。 range()函数,可以生成一个整数序列,再通过list()函数可以转换为list。 比如我们想计算1-10的整数
场景 我有一个 DevExpress XtraGrid。 显示的数据采用主/详细信息格式,点击行开头的“+”可展开该主行的详细信息。 我通过将网格数据源绑定(bind)到包含自己的字典属性(以保存详细
我是一名优秀的程序员,十分优秀!