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

时间:2016-09-12 16:34:12

标签: 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 entirely unrelated int
int j = 17;

buffer末尾的其他存储是否允许,int指向的新p对象尚未使用的部分是重新打开到堆栈并由后续自动存储持续时间对象隐式重用?换句话说,一致的实现是否允许&j == p+1

相关地,明确地重用其他存储是明确定义的行为吗?

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

也就是说,intp指向的q都在其生命周期内?

2 个答案:

答案 0 :(得分:1)

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

实施例

int a;
// do some stuff with a.

int b; 
// do some stuff with b.

假设a和b都使用堆栈,它们可以共享地址,因为编译器可以告诉它们的不同用法。

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

sub stackPointer, #AmountOfSpaceNeeded

在此示例中,空间量不会影响分配速度。

在堆上创建项目需要找到一些未使用的内存区域,或者"映射"一些新的地址空间,并且使用堆的一个(或两个带帧指针)指令的成本要花费很多倍。

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

因此,在堆中使用位置进行优化没有什么价值,以节省堆栈上的存储,

  1. 并非所有函数都使用堆
  2. 只使用堆
  3. 没有时间节省
  4. 堆后面的代码机制可能会执行测量(缓冲区溢出检测),这会导致编译器违反as-if规则。
  5. 除了确定空间未使用之外,编译器还需要确定内存的生命周期是正确的,因为在代码的函数/代码块完成之前可以销毁堆对象。

答案 1 :(得分:-2)

是的,完全没问题。

alignas(int) char buffer[2 * sizeof(int)];

在堆栈上分配8个字节。

 int* p = new (buffer) int{42};
 int* q = new (p+1) int{6};

这些新运算符不会分配内存来构造对象,而是使用缓冲区。

但请注意,这些对象的生命周期将是缓冲区变量的生命周期。当缓冲区被破坏时,p& q是参考,将被销毁。

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

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

P.S。我相信你明白,在你的情况下,管理这样的记忆会更容易;

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

image