这是我上一个问题的后续问题,在这个问题上,我似乎使问题比我最初打算的更复杂。(见问答评论中的讨论。)这个问题是对原始问题的一个轻微修改,删除了在构建/销毁封闭对象期间的特殊规则问题。
是否允许在其封闭对象的生存期内重用非静态数据成员的存储?如果允许,在什么条件下?
考虑一下程序
#include<new>
#include<type_traits>
using T = /*some type*/;
using U = /*some type*/;
static_assert(std::is_object_v<T>);
static_assert(std::is_object_v<U>);
static_assert(sizeof(U) <= sizeof(T));
static_assert(alignof(U) <= alignof(T));
struct A {
T t /*initializer*/;
U* u;
void construct() {
t.~T();
u = ::new(static_cast<void*>(&t)) U /*initializer*/;
}
void destruct() {
u->~U();
::new(static_cast<void*>(&t)) T /*initializer*/;
}
A() = default;
A(const A&) = delete;
A(A&&) = delete;
A& operator=(const A&) = delete;
A& operator=(A&&) = delete;
};
int main() {
auto a = new A;
a->construct();
*(a->u) = /*some assignment*/;
a->destruct(); /*optional*/
delete a; /*optional*/
A b; /*alternative*/
b.construct(); /*alternative*/
*(b.u) = /*some assignment*/; /*alternative*/
b.destruct(); /*alternative*/
}
除了静态断言之外,假设初始化器、析构函数和T和U的赋值不抛出。
对象类型T
和U
需要额外满足哪些条件,以便程序具有定义的行为(如果有)?
它是否取决于实际调用的的析构函数(例如,取决于是否存在/*可选*/或/*备选*/行)?。
它是否取决于A的存储时间,例如是否使用主中的/*备选*/行?
请注意,除了在析构函数和destruct
函数中,程序在新放置后不使用t
成员。当然,不允许在其存储被其他类型占用时使用它。
另请注意,程序在所有执行路径中调用其析构函数之前在t
中构造了一个原始类型的对象,因为我不允许T
和U
抛出异常。
还请注意,我不鼓励任何人编写这样的代码。我的目的是更好地理解语言的细节。特别是,我没有发现任何禁止此类放置的新闻,至少,只要没有调用析构函数。
此答案基于以下网站上提供的草稿:http://eel.is/c草稿/
我们可以尝试(通过检查每个条件)将我决定调用的“不死对象”子句应用于曾经存在的任何先前对象,这里我们将其应用于T
类型的成员t
:
寿命[基本寿命]/8
如果在对象的生存期结束后且在重用或释放该对象占用的存储之前,在原始对象占用的存储位置创建了新对象,则指向原始对象的指针、引用原始对象的引用或原始对象的名称将自动引用新对象,并且,一旦新对象的生存期开始,可以用于操纵新对象,如果:
(8.1)新对象的存储正好覆盖原始对象占用的存储位置,并且
(8.2)新对象与原始对象的类型相同(忽略顶级cv限定符),并且
(8.3)原始对象既不是const限定的完整对象,也不是此类对象的子对象,并且
(8.4)原始对象和新对象都不是潜在重叠的子对象([简介对象])。
通过在旧构件上使用新放置,条件1和2自动得到保证:
struct A {
T t /*initializer*/; (...)
void destruct() { (...)
::new(static_cast<void*>(&t)) T /*initializer*/;
}
位置相同,类型相同。这两种情况都很容易验证。
创建的对象都不是:
auto a = new A;
...
A b; /*alternative*/
是常量限定的完整对象,因此t不是常量限定的完整对象的成员。满足条件3。
现在潜在重叠的定义在对象模型[intro.object]/7中:
可能重叠的子对象是:
(7.1)基类子对象,或
(7.2)使用no_unique_address属性声明的非静态数据成员。
t
成员两者都不是并且满足条件4。
满足所有4个条件,因此可以使用成员名t
来命名新对象。
[请注意,在任何时候我都没有提到子对象不是const成员,不是它的子对象。这不是最新草案的一部分。
这意味着const子对象可以合法地更改其值,并且引用成员可以为现有对象更改其referent。这不仅令人不安,而且可能不被许多编译器支持。结束注释。]
如果一个被销毁(无论是通过删除还是不在范围内),则调用t.t(),如果t实际上不是一个t(通过不调用destruct)。
这不适用,如果
调用destruct
后,如果T
具有const
或引用成员(直到C 20),则不允许使用t
。
除此之外,就我所见,你对这个类所做的事情没有任何限制。
所有的数据都有生命周期,生命周期越长,需要的手续费(Energy)越多。生命周期终止,数据将被删除。从而可以淘汰无用数据。 日志型数据的生命周期是固定的一年,通过数据的生命周期,可以计算出日志写入时间,跨链读取将以这个时间和区块的时间比较,如果大于n*5分钟(n为两条链的逻辑距离),则日志数据是有效的。从而实现可信的跨链数据读取。
每个响应对象只有当在 servlet 的 service 方法的范围内或在 filter 的 doFilter 方法范围内是有效的,除非该组件关联的请求对象已经开启异步处理。如果相关的请求已经启动异步处理,那么直到AsyncContext 的 complete 方法被调用,请求对象一直有效。为了避免响应对象创建的性能开销,容器通常回收响应对象。在相关的请求的startAsync 还没有调用时,开发
每个请求对象只在一个 servlet 的 service 方法的作用域内,或过滤器的 doFilter 方法的作用域内有效,除非该组件启用了异步处理并且调用了请求对象的 startAsync 方法。在发生异步处理的情况下,请求对象一直有效,直到调用 AsyncContext 的 complete 方法。容器通常会重复利用请求对象,以避免创建请求对象而产生的性能开销。开发人员必须注意的是,不建议在上
对象的地址在其生命周期内是不变的还是可以改变的?我只是认为对象的地址永远不会改变。它依赖于JVM吗?我没有找到任何明确的规范。
在Spring MVC中,我可以使用ThreadLocal通过请求在不同组件之间共享数据,当请求完成时数据将被自动清除。使用WebFlux,由于一个请求可以由多个线程处理,因此该解决方案将不起作用。如何实现类似的解决方案,以便最初一个WebFilter可以在请求上下文中设置一些数据,然后可以在控制器中访问和修改数据,以及请求经过的任何事件处理程序? 我尝试了订阅者Context,但它不起作用。这是
我有两个程序。第一个分配共享内存文件,第二个从中读取。。我使用placement new将对象放置到此内存中,以确保对象不会使用新的或分配共享内存文件之外的任何内存。 我的阵列结构: 方案1: 方案2: > 程序一将SHMArray放置在内存中,位置为new。程序二在程序一已经放置的对象上做同样的事情(覆盖它)。这是未定义的行为吗?我认为不是,但我想确认一下。 两个程序都不调用析构函数数组- 我基