进入嵌套范围的自动存储的生存期



假设有一个对象具有自动存储持续时间,并且该对象是从嵌套范围内复制初始化的,就像循环体一样。从嵌套范围内创建的值的生存期是否扩展到包含范围?

#include<iostream>
using namespace std;
class Thing {
public:
int data;
Thing(int data) : data(data) { cout << "making a thing" << endl; }
~Thing() { cout << "destroying a thing" << endl; }
};
int main() {
Thing t = Thing(-1);
for (int i = 0; i < 4; i++) {
t = Thing(i); // this is both created AND destroyed from within this scope...?
}
cout << t.data << endl; // undefined behavior?
}

现在,在最后访问t.data是可行的,但我看到每个Thing的析构函数在每个循环迭代中被调用一次,所以我可能只是运气好?

这看起来很相关(但我不是律师,所以很难解读(:一些2011年的c++标准
特别是:

  1. 对于这样一个没有可变长度数组类型的对象[具有自动存储持续时间],其生存期从进入与其关联的块开始延长,直到该块的执行以任何方式结束

所以,如果我的代码片段是未定义的行为-对于循环体更改局部变量,应该手动堆分配该局部变量,然后手动释放,或者。。。?

这是完全合法的、定义的行为。

您的错误在于认为在块内创建的Thing的寿命很重要。事实并非如此。复制后,分配给t的副本并没有改变其的内容;它仍然是块外创建的t,只是更新了值。它的寿命完全不受影响。在您引用的上下文中;与其[t]相关联的块";是顶级函数作用域(声明它的位置(,而不是在其中复制分配它的块。

在块内创建的Thing每次都会过期,但这没关系;它是从中复制的,然后再也没有使用过。

这里没有未定义的行为:

#include<iostream>
class Thing {
public:
int data;
Thing(int data) : data(data) { std::cout << "making a thing" << data << std::endl; }
~Thing() { std::cout << "destroying a thing" << data << std::endl; }
Thing& operator=(const Thing &t) { data = t.data; std::cout << "operator=" << std::endl; return *this; }
};
int main() {
Thing t = Thing(-1); // create object t
for (int i = 0; i < 4; i++) {
t = Thing(i);
// 1. create an object Thing(i)
// 2. use operator= to copy that object into the object t
// 3. Thing(i) object gets destroyed at the end of scope
}
// object t is still valid, but it was modified in the loop with operator=
cout << t.data << endl; // no undefined behavior here
}

您可以将复制赋值运算符(operator=(成员函数添加到Thing类中以进行检查。在您的情况下,这个运算符是由编译器根据标准中的规则隐式添加的:

如果没有为类提供用户定义的复制赋值运算符类型(结构、类或联合(,编译器将始终声明一个作为类的内联公共成员。此隐式声明的副本赋值运算符的形式为T&T: :operator=(const T&(如果所有以下是正确的:

  1. T的每个直接基B都有一个复制赋值运算符,其参数是B或常量B&或常量易失性B&
  2. 每个非静态数据类类型的T的成员M或类类型的数组具有副本参数为M或常量M&或const波动性并购

最新更新