C++ 如果生命周期结束,是否允许隐式或显式重用对象存储?

C++ 如果生命周期结束,是否允许隐式或显式重用对象存储?,c++,language-lawyer,lifetime,c++17,C++,Language Lawyer,Lifetime,C++17,考虑以下示例: // create some storage alignas(int) char buffer[2 * sizeof(int)]; // new object of type int at the storage of buffer, the int pointed // to by p begins its lifetime here, buffer's lifetime is over int* p = new (buffer) int{42}; // some enti

考虑以下示例:

// create some storage
alignas(int) char buffer[2 * sizeof(int)];

// new object of type int at the storage of buffer, the int pointed
// to by p begins its lifetime here, buffer's lifetime is over
int* p = new (buffer) int{42};

// some entirely unrelated int
int j = 17;
p
指向的新
int
对象尚未使用的
buffer
末尾的另一个存储是否允许重新打开堆栈,并被自动存储持续时间的后续对象隐式重用?换句话说,是否允许一致性实现具有
&j==p+1


与此相关的是,明确重用其他存储是否是定义良好的行为

alignas(int) char buffer[2 * sizeof(int)];
int* p = new (buffer) int{42};
int* q = new (p+1) int{6}; 

也就是说,
p
q
指向的
int
s是否仍在其生命周期内

是的,很好

alignas(int) char buffer[2 * sizeof(int)];
在堆栈上分配8个字节

 int* p = new (buffer) int{42};
 int* q = new (p+1) int{6};
这些新操作符不分配内存来构造对象,而是使用缓冲区

但请注意,这些对象的生存期将是缓冲区变量的生存期。当缓冲区被销毁时,p&q引用的内存将被销毁

同样在一般情况下,您假设在销毁缓冲区之前手动调用析构函数

因此,“如果生命周期结束,是否允许隐式或显式重用对象存储?”问题的答案是No。当缓冲区的生命周期结束时,继续使用它的内存是不安全的,因为稍后它将用于其他堆栈变量

另外,我相信你明白,在你的情况下,像这样管理内存会更容易

int buffer[2];
int* p = &buffer[0];
int* q = &buffer[1];

编译器不必为堆栈中的每个变量使用唯一的内存区域,只要它能确定程序不能测量重复使用-例如,检查两个变量是否具有相同的地址,可能会导致编译器确保它们没有共享内存

范例

int a;
// do some stuff with a.

int b; 
// do some stuff with b.
假设a和b都使用堆栈,它们可以共享地址,因为编译器可以告诉它们不同的用法

在堆栈上创建项是一条简单的指令

sub stackPointer, #AmountOfSpaceNeeded
在本例中,空间量不影响分配速度

在堆上创建项需要查找一些未使用的内存区域,或者在其中“映射”一些新的地址空间,并且需要使用堆的一条(或两条带帧指针的)指令的成本的许多倍

它还可能在内存检查器上产生误报,即对对象中可用的空闲内存量存在分歧

因此,优化使用堆中的一个位置以节省堆栈上的存储,如

  • 并非所有函数都使用堆
  • 仅使用堆不会节省时间
  • 堆后面的代码机制可能会执行测量(缓冲区溢出检测),这会导致编译器破坏
    规则,就像
    规则一样
  • 除了确定空间未使用外,编译器还需要确定内存的使用寿命是否正确,因为堆对象可能在函数/代码块完成之前被销毁

  • 如果编译器可以证明“p+1”不会被应用程序使用,那么如果它使用了该内存,您会如何注意(除了通过进行UB指针比较)呢?@Barry引用被替换为类型为T的对象o的生存期,当[…]对象占用的存储被释放时,或者被不嵌套在其他对象中的对象重用“存储”和“对象生命周期”以及“重用”的概念在我上一次标准研究时非常模糊,以至于无法使用,而不是“您知道它应该如何工作,它以这种方式工作”的解释。在实践中,自动重用存储将是疯狂的,而实际实现(如variant/any/std function/etc等代码)要求您可以获取一些存储,将其用于对象A,销毁对象A,然后将相同的存储重新用于对象B,而不仅仅是存储A。我知道C++17有一些修复,但。。。我不会抱希望的。@hyde嗯,
    p->~int();新的(缓冲区)int[2]{1,2}转到并使用缓冲区的第二部分。如果您需要重写
    j
    @Yakk,这可能会令人惊讶。您不能将
    char*
    分配给
    int*
    。这回答了问题的第二部分,但忽略了问题的第一部分。OP的示例如何?对于变量
    j
    ?这是不正确的,因为对象具有明确的生存期。它们必须手动
    delete
    d。它不绑定到缓冲区,但必须保持在缓冲区生存期内。