当我有一个循环并在此循环内时,创建一个新的堆栈变量(不将其分配到堆上,也不将其持有的变量保持在循环体内声明),是否保证此对象的析构函数在下一次迭代开始之前被调用,否则可能循环展开由编译器更改有关的东西吗?
当我有一个循环并在此循环内时,创建一个新的堆栈变量(不将其分配到堆上,也不将其持有的变量保持在循环体内声明),是否保证此对象的析构函数在下一次迭代开始之前被调用,否则可能循环展开由编译器更改有关的东西吗?
Answers:
来自n4800
:
§6.3.3 块范围:
在块(8.3)中声明的名称是该块的本地名称;它具有块范围。它的潜在范围从其声明点(6.3.2)开始,到其块末尾结束。在块作用域中声明的变量是局部变量。
§10.3.6 析构函数:
当创建对象的块退出时,析构函数将隐式调用[...](8.7)
§4.1.1 摘要机:
这项规定有时称为“按原样”规则,因为只要可以从以下行为的可观察行为中确定,只要实现可以遵循该要求,则实现可以自由地忽略本文档的任何要求。该程序。
[强调我的]
所以,是的。您的变量在循环末尾(是一个块)超出范围,因此,只要观察程序行为的人都能知道,则调用其析构函数。
call
ed。或者,如果它们有效地(按规则)不执行任何操作,则可能没有针对此类析构函数生成的程序集。
是。当您考虑在其中声明变量的“块”(即,在一对大括号之间)时,更容易可视化。该循环本身就是一个块,当它到达结束括号时,在下一次迭代之前,将调用循环中声明的所有自动存储变量的析构函数。
可能会由编译器循环展开,从而对此有所改变?
根据经验,不要考虑编译器将优化什么,因为无论优化它做什么,它仍然需要保证程序的行为。在这种情况下,如果发生循环展开,则不会改变任何效果。
[class.copy.elision]
[stmt.iter]
它是完全等效的(强调我的意思):“如果迭代语句中的子语句是单个语句而不是复合语句,则好像它被重写为包含以下内容的复合语句一样:原始声明。 ”。从本质上讲,为单个语句使用或不使用括号都意味着完全相同的事情,并且括号是隐式的。为了清楚起见,我省略了它。
每次迭代都调用析构函数。因此,在某些情况下,在循环外部而不是在循环中声明变量会更快。假设以下情况:
std::string temp;
for(int i = 0; i < 10; ++i){
temp = arr[i];
doSomething(temp);
}
使用循环执行时不调用析构函数。它只是覆盖temp
。
但是,如果使用std::string temp = arr[i]
构造函数,则每次迭代都会调用析构函数。我认为如果您有一个经常执行的循环,这会增加一点运行时间。
当然,在迭代结束时调用dtor,并且展开循环不应修改此行为,就像进行其他任何优化(优化不应修改程序行为)一样,除了某些RVO之类的方法(它可以消除一些语义上虚假的对象创建) 。