C++ 编译器能否从堆到堆栈分配进行优化?
就编译器优化而言,将堆分配更改为堆栈分配是否合法和/或可能?或者这会打破僵局吗 例如,假设这是代码的原始版本C++ 编译器能否从堆到堆栈分配进行优化?,c++,memory-management,language-lawyer,compiler-optimization,C++,Memory Management,Language Lawyer,Compiler Optimization,就编译器优化而言,将堆分配更改为堆栈分配是否合法和/或可能?或者这会打破僵局吗 例如,假设这是代码的原始版本 { Foo* f = new Foo(); f->do_something(); delete f; } 编译器是否能够将其更改为以下内容 { Foo f{}; f.do_something(); } 我不这么认为,因为如果原始版本依赖于自定义分配器之类的东西,这将产生影响。标准中有没有具体说明这一点?这些都不是等效的f.do_somet
{
Foo* f = new Foo();
f->do_something();
delete f;
}
编译器是否能够将其更改为以下内容
{
Foo f{};
f.do_something();
}
我不这么认为,因为如果原始版本依赖于自定义分配器之类的东西,这将产生影响。标准中有没有具体说明这一点?这些都不是等效的
f.do_something()
可能抛出,在这种情况下,第一个对象保留在内存中,第二个对象被销毁。是的,这是合法的expr.delete/7
:
如果删除表达式的操作数的值不是null
指针值,然后:
-如果对象的新表达式的分配调用
删除未省略,分配未扩展(5.3.4),
delete表达式应调用释放函数(3.7.4.2)。
从新表达式的分配调用返回的值
应作为第一个参数传递给解除分配函数
-否则,如果分配被延长或由
扩展另一个新表达式的分配,以及
删除由
新表达式,该表达式具有扩展
已计算新表达式,删除表达式应调用
释放函数。从的分配调用返回的值
扩展的新表达式应作为第一个参数传递给
释放函数
-否则,删除表达式将不会调用解除分配
功能(3.7.4.2)
总之,用实现定义的东西替换new
和delete
是合法的,比如使用堆栈而不是堆
注意:正如Massimiliano Jane的评论,编译器无法准确地为您的示例执行此转换,如果
do_something
抛出:在这种情况下,编译器应该省略f
的析构函数调用(而在这种情况下,转换后的示例确实调用析构函数)。但除此之外,它可以自由地将f
放入堆栈。我想指出一些我在其他答案中没有强调的问题:
struct Foo {
static void * operator new(std::size_t count) {
std::cout << "Hey ho!" << std::endl;
return ::operator new(count);
}
};
structfoo{
静态void*运算符新(std::size\u t count){
std::cout No,那太过分了。堆栈使用率的增长是一个大问题,他们确实以它命名了一个流行的编程网站。相关:Clang确实优化了这个iff,它可以内联调用的函数(+函数体上的某些条件).从tobi303提到的链接来看,自c++14以来,情况发生了变化,请参见];从c++14开始,编译器可以在堆栈中存储Foo,只要它能够证明相同的行为(例如,没有任何东西被抛出do_something)值得注意的是,声明函数noexcept
对gcc和clang的优化器没有帮助,但函数体显示clang却有帮助。这可能还有更多。@BaummitAugen,如果你问“编译器为什么不执行此优化?”我认为还有更多:当有人编写一个新表达式时,他们需要动态分配。如果他们需要堆栈分配,他们应该编写Foo f{}
。这是有正当理由的,编译器无法知道,例如,可能他们在valgrind下运行,想要跟踪所有堆的使用情况,或者他们正在调试堆碎片问题。编译器必须在允许的优化和程序员真正想要的之间取得平衡。编译器应该是朋友,而不是朋友问题的一个关键是,即使使用了new
,分配是否也可以在堆栈上。如果我理解正确,它将始终是动态内存,而不会在堆栈上。给定的段落说,分配的大小可以扩展到更大的动态内存块,或者使用以前的动态内存扩展。@SHR:我强调了“存储由实现提供”。它可以是任何东西,甚至是堆栈。这些更改的背景在我对