Raz*_*ius 4 c++ memory-management allocation
很抱歉,如果在标准中明确说明之前已经询问过,但我无法找到自动存储的对象的内存是在封闭块的开头还是在执行构造函数之前分配?
我问这个是因为https://en.cppreference.com/w/cpp/language/storage_duration说的。
存储期 程序中的所有对象都具有以下存储期之一:
自动存储时间。对象的存储在封闭代码块的开头分配,并在结尾释放。所有本地对象都有这个存储持续时间,除了那些声明为 static、extern 或 thread_local 的对象。
现在,这是否意味着即使由于某种原因没有调用构造函数,也会分配存储空间?
例如,我有这样的事情。
{
if(somecondition1) throw something;
MyHugeObject o{};
/// do something
}
Run Code Online (Sandbox Code Playgroud)
因此,有可能不需要构造 MyHugeObject,但根据我引用的来源,尽管该对象可能永远不会被构造,但仍为其分配了内存。是这种情况还是基于实现的东西?
首先,从语言标准的角度来看,你不能在对象的生命周期之外访问对象的存储。在创建对象之前,您不知道对象所在的位置,并且在它被销毁后,访问存储会产生未定义的行为。简而言之:符合标准的 C++ 程序无法观察分配存储时间的差异。
自动存储通常意味着“在调用堆栈上”。即分配是通过递减堆栈指针发生的,而解除分配则是重新递增它。编译器可以发出代码,在对象的生命周期开始/结束的地方准确地调整堆栈指针,但这是低效的:它会使生成的代码混乱,每个使用的对象都有两条额外的指令。对于在循环中创建的对象,这尤其是一个问题:堆栈指针会不断地在两个或多个位置之间来回跳跃。
为了提高效率,编译器将所有可能的对象分配集中到一个堆栈帧分配中:编译器为函数内的每个变量分配一个偏移量,确定最大值。存储函数中存在的所有变量所需的大小,并在函数执行开始时使用单个堆栈指针递减指令分配所有内存。清理然后是相应的堆栈指针增量。这消除了循环中的任何分配/解除分配开销,因为下一次迭代中的变量将简单地重用堆栈帧中与前一次迭代使用的相同位置。这是一项重要的优化,因为许多循环至少声明一个变量。
C++ 标准并不关心。由于在对象生命周期之外使用存储是 UB,编译器可以随意使用存储。程序员也不应该关心,但他们确实倾向于关心他们的程序执行时间。这就是大多数编译器通过使用堆栈帧分配来优化的。