首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Lifetime

每一个对象和参照系有一个寿命,它是一个运行时属性:对于任何对象或引用,程序的生命周期开始时都有一个执行点,并且有一个结束的时刻。

  • 类或聚合类型的任何对象,如果它或它的任何子对象由除平凡默认构造函数,生命周期从初始化结束时开始。
  • 类类型的任何对象的破坏者并不简单,当析构函数的执行开始时,生命周期就会结束。
  • 成员的寿命联合当该成员处于活动状态时开始。
  • 对于由普通默认构造函数、非类对象、数组等初始化的所有其他对象%28类对象,生存期从为对象分配正确对齐存储时开始,当存储被另一个对象解除分配或重用时结束。

对象的生存期等于或嵌套在其存储的生存期内,请参见存储时间...

Lifetime of a reference is exactly its storage duration.

(until C++14)

The lifetime of a reference begins when its initialization is complete and ends as if it were a scalar object.

(since C++14)

注意:引用对象的生存期可能在引用的生存期结束之前结束,这将使悬空引用有可能。

成员对象和基子对象的生命周期开始和结束类初始化顺序...

临时对象寿命

时创建临时对象。物化因此,它可以作为glvalue使用,在以下情况下,自C++17%29以来,它会发生%28:

  • 绑定对prvalue的引用从创建prvalue lambda表达式的函数转换返回prvalue值,%28,因为C++11%29复制初始化要求转换初始化器,列表初始化,构造std::initiizer[医]列表,%28自C++11%29引用-初始化到不同但可转换的类型或位字段。%28直到C++17%29初始化类型为std::initiizer的对象时[医]列单<T>在执行数组到指针转换时对类prvalue执行成员访问时,从大括号到init-列表中,或者在未计算的操作数上订阅未计算的操作数时,当一个prvalue显示为一个丢弃的值表达式时,如果实现支持的话。在函数调用表达式%28中传递或返回可复制类型的对象时,此模型传递cpu寄存器%29中的结构。%28自C++17%29
  • 从函数返回prvalue
  • 转换,它创建了一个prvalue。
  • Lambda表达,%28自C++11%29
  • 复制初始化它需要对初始化程序进行转换,
  • 列表初始化构造一个std::initializer_list,%28自C++11%29
  • 参考初始化转换为不同但可转换的类型或位字段。

%28直到C++17%29

  • 何时初始化类型对象std::initializer_list<T>从带括号的列表中
  • 表演时成员访问关于类prvalue
  • 在执行数组对指针转换或订阅在数组prvalue上
  • 中未评估的操作数大小和类型
  • 当prvalue显示为废弃值表达式
  • 如果得到实现的支持,则在传递或返回函数调用表达式%28这个模型在CPU寄存器中传递结构%29

%28自C++17%29

The materialization of a temporary object is generally delayed as long as possible in order to avoid creating unnecessary temporary object: see copy elision.

(since C++17)

所有临时对象作为计算%28词汇%29包含创建它们的点的完整表达式的最后一步被销毁,如果创建了多个临时对象,则按与创建顺序相反的顺序销毁它们。即使该评估以抛出异常结束,也是如此。

有两个例外:

  • 临时对象的生存期可以通过绑定到Const值引用或rvalue引用%28来扩展,因为C++11%29,请参见参考初始化关于细节。在计算用于初始化数组元素的默认构造函数的默认参数时创建的临时对象的生存期,然后数组的下一个元素开始初始化。%28自C++11%29
  • 在计算用于初始化数组元素的默认构造函数的默认参数时创建的临时对象的生存期,然后数组的下一个元素开始初始化。

%28自C++11%29

存储重用

如果对象是,则不需要程序调用对象的析构函数来结束其生命周期。可摧毁的或者如果程序不依赖于析构函数的副作用。但是,如果一个程序结束了一个非平凡对象的生命周期,它必须确保一个相同类型的新对象被构造为就地%28例如。通过在析构函数被隐式调用之前放置新的%29,即由于自动对象的范围退出或异常、线程本地对象的线程退出或静态对象的程序退出;否则行为是未定义的。

二次

代码语言:javascript
复制
class T {}; // trivial
struct B {
    ~B() {} // non-trivial
};
void x() {
    long long n; // automatic, trivial
    new (&n) double(3.14); // reuse with a different type okay
} // okay
void h() {
    B b; // automatic non-trivially destructible
    b.~B(); // end lifetime (not required, since no side-effects)
    new (&b) T; // wrong type: okay until the destructor is called
} // destructor is called: undefined behavior

二次

重用静态、线程本地或自动存储时间的Const完整对象占用的存储是未定义的行为,因为这些对象可能存储在只读内存中。

二次

代码语言:javascript
复制
struct B {
    B(); // non-trivial
    ~B(); // non-trivial
};
const B b; // const static
void h() {
    b.~B(); // end the lifetime of b
    new (const_cast<B*>(&b)) const B; // undefined behavior: attempted reuse of a const
}

二次

如果在另一个对象占用的地址上创建了一个新对象,那么所有指针、引用和原始对象的名称都将自动引用新对象,并且一旦新对象的生存期开始。可用于操作新对象,但必须满足以下条件:

  • 新对象的存储完全覆盖了原始对象占用的存储位置。
  • 新对象的类型与原始对象%28相同,忽略顶级cv-限定符%29。
  • 原始对象的类型不是const限定的。
  • 如果原始对象具有类类型,则它不包含任何非静态数据成员,其类型为const限定或引用类型。
  • 原始对象是类型T的派生对象最多的对象,而新对象是T%28类型的派生对象最多的对象,也就是说,它们不是基类子对象%29。

二次

代码语言:javascript
复制
struct C {
  int i;
  void f();
  const C& operator=( const C& );
};
const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C();          // lifetime of *this ends
    new (this) C(other); // new object of type C created
    f();                 // well-defined
  }
  return *this;
}
C c1;
C c2;
c1 = c2; // well-defined
c1.f();  // well-defined; c1 refers to a new object of type C

二次

If the conditions listed above are not met, a valid pointer to the new object may still be obtained by applying the pointer optimization barrier std::launder. Similarly, if an object is created in the storage of a class member or array element, the created object is only a subobject (member or element) of the original object's containing object if: the lifetime of the containing object has begun and not ended the storage for the new object exactly overlays the storage of the original object the new object is of the same type as the original object (ignoring cv-qualification). Otherwise (such as if the subobject contains a reference member or a const subobject), the name of the original subobject cannot be used to access the new object without std::launder: struct X { const int n; }; union U { X x; float f; }; void tong() { U u = { { 1 } }; u.f = 5.f; // OK, creates new subobject of 'u' X *p = new (&u.x) X {2}; // OK, creates new subobject of 'u' assert(p->n == 2); // OK assert(*std::launder(&u.x.n) == 2); // OK assert(u.x.n == 2); // undefined: 'u.x' does not name the new subobject } As a special case, objects can be created in arrays of unsigned char or std::byte (in which case it is said that the array provides storage for the object) if. the lifetime of the array has begun and not ended the storage for the new object fits entirely within the array there is no smaller array object that satisfies these constraints. If that portion of the array previously provided storage for another object, the lifetime of that object ends because its storage was reused, however the lifetime of the array itself does not end (its storage is not considered to have been reused). template<typename ...T> struct AlignedUnion { alignas(T...) unsigned char datamax(sizeof(T)...); }; int f() { AlignedUnion<int, char> au; int *p = new (au.data) int; // OK, au.data provides storage char *c = new (au.data) char(); // OK, ends lifetime of *p char *d = new (au.data + 1) char(); return *c + *d; // OK }

(since C++17)

  • 包含对象的生存期已开始,但未结束。
  • 新对象的存储完全覆盖了原始对象的存储。
  • 新对象与原始对象%28的类型相同,忽略cv-资格%29。

否则,%28,例如如果子对象包含引用成员或Const子对象%29,则不能使用原始子对象的名称访问新对象。std::launder*

结构X{const int n;};uniu{x;Float f;};void路%28%29{u={1};uf=5.f;//OK,创建%27u%27 X的新子对象。%2AP=新%28&u。x%29X{2};//OK,创建%27u%27断言%28p->n=2%29;//OK断言%28的新子对象%2A性病:洗衣%28&u。X.N%29==2%29;//OK断言%28 u。X.N=2%29;//未定义:%27u。X%27没有命名新的子对象}

作为特例,可以在unsigned charstd::byte%28在这种情况下,据说数组提供存储对于对象%29,如果。

  • 数组的生存期已经开始,但没有结束。
  • 新对象的存储完全适合于数组。
  • 没有满足这些约束的较小的数组对象。

如果数组中以前为另一个对象提供存储的那一部分,则该对象的生存期结束是因为它的存储被重用,但是数组本身的生存期不结束%28--它的存储被认为已被重用%29。

模板<type Name...T>struct AlignedUnion{alias%28t...%29无符号字符数据最大值%28%28%29...%29;;INT f%28%29{AlignedUnion<int,char>au;int%2AP=新的%28 au.data%29 int;//OK,au.data提供存储字符%2AC=新的%28 au.data%29 char%28%29;//OK,结束%2AP焦%2AD=新%28 au.data+1%29 char%28%29;返回%2AC+%2Ad;//OK}

%28自C++17%29

生命期外访问

在对象的生存期开始之前,但在对象将要占用的存储被分配之后,或者在对象的生存期结束之后,或者在对象占用的存储被重用或释放之前。标识该对象的glvalue表达式的下列用法未定义:

  1. L值到rvalue转换%28等。函数调用值%29的函数。
  1. 对非静态数据成员的访问或对非静态成员函数的调用。
  1. 绑定到对虚拟基类子对象的引用。
  1. dynamic_casttypeid表情。

上述规则也适用于指针,也适用于%28绑定--将对虚拟基的引用替换为将指针隐式转换为指向虚拟基%29的指针,并附加两个规则:

  1. static_cast只有在转换到%28(可能是cv-限定%29)时,才允许指向没有对象的存储指针。void*...
  1. 指向没有对象的存储的指针,该对象可能是cv限定的。void*只能static_cast指向可能符合cv条件的指针。char,可能是cv-符合条件的unsigned char,或者可能是cv-符合条件的。std::byte...

在建造和破坏期间,适用其他限制,见虚拟函数在构造和破坏过程中的调用...

注记

在以下示例中,非类对象的生存期结束规则%28结束存储持续时间%29和类对象%28反向构造顺序%29之间的差异很重要:

二次

代码语言:javascript
复制
struct A {
  int* p;
  ~A() { std::cout << *p; } // if n outlives a, prints 123
};
void f() {
  A a;
  int n = 123; // if n does not outlive a, this is optimized out (dead store)
  a.p = &n;
}

二次

缺陷报告

以下行为更改缺陷报告追溯应用于先前发布的C++标准。

DR

Applied to

Behavior as published

Correct behavior

CWG 2012

C++14

lifetime of references was specified to match storage duration, requiring thatextern references are alive before their initializers run

lifetime begins at initialization

另见

c终身文件

*。

代码语言:txt
复制
 ? cppreference.com

在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com