492 lines
50 KiB
HTML
492 lines
50 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh" dir="ltr" class="client-nojs" xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh">
|
|
<head><meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>生存期</title>
|
|
<link rel="stylesheet" href="ext.css" />
|
|
<link rel="stylesheet" href="site_modules.css" />
|
|
</head>
|
|
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_lifetime skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">生存期</h1>
|
|
<div id="bodyContent">
|
|
<div id="contentSub"><span class="subpages">< <a href="cpp.html">cpp</a>‎ | <a href="cpp-language.html">language</a></span></div>
|
|
<div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr" xml:lang="zh">
|
|
<p>每个<a href="cpp-language-object.html">对象</a>和<a href="cpp-language-reference.html">引用</a>都有<i>生存期 (lifetime)</i> ,这是一项运行时性质:每个对象或引用在程序执行时都存在一个时刻开始其生存期,也都存在一个时刻结束其生存期。</p>
|
|
<p>对象的生存期始于:</p>
|
|
<ul>
|
|
<li>获得拥有其类型的正确大小与对齐的存储,并且</li>
|
|
<li>完成其初始化(若存在)(包括不经由构造函数或经由<a href="cpp-language-default_constructor.html#.E5.B9.B3.E5.87.A1.E9.BB.98.E8.AE.A4.E6.9E.84.E9.80.A0.E5.87.BD.E6.95.B0">平凡默认构造函数</a>的<a href="cpp-language-default_initialization.html">默认初始化</a>),除非</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>若该对象是<a href="cpp-language-union.html#.E6.88.90.E5.91.98.E7.94.9F.E5.AD.98.E6.9C.9F">联合体成员</a>或其子对象,则其生存期在该联合体成员是联合体中的被初始化成员,或它被设为活跃才开始,或者</li>
|
|
<li>若该对象内嵌于联合体对象,则其生存期开始于平凡特殊成员函数赋值或构造含有它的联合体对象,或者</li>
|
|
<li>数组对象的生存期可以因为该对象被 <span class="t-lc"><a href="cpp-memory-allocator-allocate.html">std::allocator::allocate</a></span> 分配而开始。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<p>某些操作在给定的存储区域中<a href="cpp-language-object.html#.E5.AF.B9.E8.B1.A1.E5.88.9B.E5.BB.BA">隐式创建对象</a>,并开始其生存期,这些对象拥有隐式生存期类型(见后述)。</p>
|
|
<p>对象的生存期结束于:</p>
|
|
<ul>
|
|
<li>若它拥有非类类型,则为销毁该对象时(可能经由伪析构函数调用),或者</li>
|
|
<li>若它拥有类类型,则为<a href="cpp-language-destructor.html">析构函数</a>调用开始时,或者</li>
|
|
<li>该对象所占据的存储被释放,或被不内嵌于它的对象所重用。</li>
|
|
</ul>
|
|
<p>对象的生存期与它的存储的生存期相同,或者内嵌于其中,参见<a href="cpp-language-storage_duration.html">存储期</a>。</p>
|
|
<p><a href="cpp-language-reference.html">引用</a>的生存期,从其初始化完成之时开始,并与标量对象以相同的方式结束。</p>
|
|
<p>注意:被引用对象的生存期可能在引用的生存期结束之前就会结束,这会造成<a href="cpp-language-reference.html#.E6.82.AC.E5.9E.82.E5.BC.95.E7.94.A8">悬垂引用</a>。</p>
|
|
<p>非静态数据成员和基类子对象的生存期,按照<a href="cpp-language-initializer_list.html#.E5.88.9D.E5.A7.8B.E5.8C.96.E9.A1.BA.E5.BA.8F" class="mw-redirect">类初始化顺序</a>开始和结束。</p>
|
|
<h3><span class="mw-headline" id=".E9.9A.90.E5.BC.8F.E7.94.9F.E5.AD.98.E6.9C.9F.E7.B1.BB.E5.9E.8B">隐式生存期类型</span></h3>
|
|
<p>一个对象类型被称为<i>隐式生存期类型</i>,若它是:</p>
|
|
<ul>
|
|
<li>标量类型,或</li>
|
|
<li>数组类型,或</li>
|
|
<li><a href="cpp-language-aggregate_initialization.html">聚合类</a>类型,或</li>
|
|
<li>拥有下列成员的类类型</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>至少一个平凡的合格构造函数,以及</li>
|
|
<li>一个平凡且未被删除的析构函数,</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<ul>
|
|
<li>或上述类型之一的 cv 限定版本。</li>
|
|
</ul>
|
|
<p>注意若隐式创建的对象的子对象不拥有隐式生存期类型,则其生存期不会隐式开始。</p>
|
|
<h3><span class="mw-headline" id=".E4.B8.B4.E6.97.B6.E5.AF.B9.E8.B1.A1.E7.9A.84.E7.94.9F.E5.AD.98.E6.9C.9F">临时对象的生存期</span></h3>
|
|
<p>在下列情况中<span class="t-rev-inl t-since-cxx17"><span>进行纯右值的<a href="cpp-language-implicit_conversion.html#.E4.B8.B4.E6.97.B6.E9.87.8F.E5.AE.9E.E8.B4.A8.E5.8C.96">实质化</a>,从而能将它作为泛左值使用,即</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>创建临时对象:</p>
|
|
<ul>
|
|
<li><a href="cpp-language-reference_initialization.html">绑定引用到纯右值</a></li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li>以花括号初始化器列表<a href="cpp-language-list_initialization.html">初始化</a> <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="cpp-utility-initializer_list.html"><span class="kw960">std::<span class="me2">initializer_list</span></span></a><span class="sy1"><</span>T<span class="sy1">></span></span></span> 类型的对象时</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-until-cxx17">
|
|
<td>
|
|
<ul>
|
|
<li>函数返回纯右值</li>
|
|
<li>创建纯右值的<a href="cpp-language-expressions.html#.E8.BD.AC.E6.8D.A2">类型转换</a>,<a href="cpp-language-explicit_cast.html">包括</a> <span class="t-c"><span class="mw-geshi cpp source-cpp">T<span class="br0">(</span>a,b,c<span class="br0">)</span></span></span> 与 <span class="t-c"><span class="mw-geshi cpp source-cpp">T<span class="br0">{</span><span class="br0">}</span></span></span>),</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<ul>
|
|
<li><a href="cpp-language-lambda.html">lambda 表达式</a></li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>要求对初始化器进行类型转换的<a href="cpp-language-copy_initialization.html">复制初始化</a>,</li>
|
|
<li><a href="cpp-language-reference_initialization.html">将引用绑定</a>到不同但可以转换的类型,或绑定到位域。</li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></td>
|
|
</tr>
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<ul>
|
|
<li>当对类类型的纯右值进行<a href="cpp-language-operator_member_access.html#.E5.86.85.E5.BB.BA.E7.9A.84.E6.88.90.E5.91.98.E8.AE.BF.E9.97.AE.E8.BF.90.E7.AE.97.E7.AC.A6">成员访问</a>时</li>
|
|
<li>当对数组纯右值进行<a href="cpp-language-array.html#.E6.95.B0.E7.BB.84.E5.88.B0.E6.8C.87.E9.92.88.E9.80.80.E5.8C.96">数组向指针</a>转换或者<a href="cpp-language-operator_member_access.html#.E5.86.85.E5.BB.BA.E7.9A.84.E4.B8.8B.E6.A0.87.E8.BF.90.E7.AE.97.E7.AC.A6">下标运算</a>时</li>
|
|
<li>对 <a href="cpp-language-sizeof.html"><code>sizeof</code></a> 和 <a href="cpp-language-typeid.html"><code>typeid</code></a> 的不求值操作数</li>
|
|
<li>当纯右值被用作<a href="cpp-language-expressions.html#.E5.BC.83.E5.80.BC.E8.A1.A8.E8.BE.BE.E5.BC.8F">弃值表达式</a>时</li>
|
|
<li>如果实现支持的话,在<a href="cpp-language-operator_other.html#.E5.86.85.E5.BB.BA.E7.9A.84.E5.87.BD.E6.95.B0.E8.B0.83.E7.94.A8.E8.BF.90.E7.AE.97.E7.AC.A6">函数调用表达式</a>中传递或者返回<a href="cpp-named_req-TriviallyCopyable.html"><span style="font-style:italic">可平凡复制</span> <span style="font-family: Georgia, 'DejaVu Serif', serif; font-style:italic">(TriviallyCopyable)</span></a> 的类型的对象(这对应的是在 CPU 寄存器中传递结构体的情况)时</li>
|
|
<li>临时对象的实质化通常会尽可能地被推迟,以免创建不必要的临时对象:参见<a href="cpp-language-copy_elision.html">复制消除</a></li>
|
|
</ul>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>所有临时对象的销毁都是在(词法上)包含创建它的位置的全表达式的求值过程的最后一步进行的,而当创建了多个临时对象时,它们是以其创建的相反顺序销毁的。即便求值过程以抛出异常而终止也是如此。</p>
|
|
<p>对此有两种例外情况:</p>
|
|
<ul>
|
|
<li>可以通过绑定到 const 左值引用<span class="t-rev-inl t-since-cxx11"><span>或右值引用</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>来延长临时对象的生存期,细节见<a href="cpp-language-reference_initialization.html#.E4.B8.B4.E6.97.B6.E9.87.8F.E7.94.9F.E5.AD.98.E6.9C.9F">引用初始化</a>。</li>
|
|
<li>在对数组的某个元素使用含有默认实参的默认或复制构造函数进行初始化时,对该默认实参求值所创建的临时对象的生存期将在该数组的下一个元素的初始化开始之前终止。</li>
|
|
</ul>
|
|
<h3><span class="mw-headline" id=".E5.AD.98.E5.82.A8.E7.9A.84.E9.87.8D.E7.94.A8">存储的重用</span></h3>
|
|
<p>如果对象<a href="cpp-language-destructor.html#.E5.B9.B3.E5.87.A1.E6.9E.90.E6.9E.84.E5.87.BD.E6.95.B0">可平凡析构</a>,或者程序并不依赖析构函数中的副作用的话,程序不必调用该对象的析构函数。然而如果程序显式终止非可平凡析构对象的生存期的话,它必须确保在可能隐式地调用析构函数前(例如对于自动对象是由于退出作用域或发生异常,对于线程局部对象是由于线程退出,或对于静态对象是由于程序退出),原位构造(比如使用布置 new )一个新的同类型对象;否则行为未定义。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">class</span> T <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 平凡</span>
|
|
<span class="kw1">struct</span> B <span class="br0">{</span>
|
|
~B<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span> <span class="co1">// 非平凡</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> x<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw4">long</span> <span class="kw4">long</span> n<span class="sy4">;</span> <span class="co1">// 自动、平凡</span>
|
|
new <span class="br0">(</span><span class="sy3">&</span>n<span class="br0">)</span> <span class="kw4">double</span><span class="br0">(</span><span class="nu16">3.14</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 以不同的类型进行重用没有问题</span>
|
|
<span class="br0">}</span> <span class="co1">// OK</span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
B b<span class="sy4">;</span> <span class="co1">// 自动的非可平凡析构对象</span>
|
|
b.~B<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 生存期结束(不必要,因为没有副作用)</span>
|
|
new <span class="br0">(</span><span class="sy3">&</span>b<span class="br0">)</span> T<span class="sy4">;</span> <span class="co1">// 类型错误:直到析构函数被调用之前都没问题</span>
|
|
<span class="br0">}</span> <span class="co1">// 调用了析构函数:未定义行为</span></pre></div>
|
|
</div>
|
|
<p>重用某个具有静态、线程局部或者自动存储期的 const 完整对象所占据的存储是未定义行为,因为这种对象可能被存储于只读内存中。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> B <span class="br0">{</span>
|
|
B<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 非平凡</span>
|
|
~B<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 非平凡</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">const</span> B b<span class="sy4">;</span> <span class="co1">// const 静态对象</span>
|
|
<span class="kw4">void</span> h<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
b.~B<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// b 的生存期结束</span>
|
|
new <span class="br0">(</span><span class="kw1">const_cast</span><span class="sy1"><</span>B<span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span><span class="sy3">&</span>b<span class="br0">)</span><span class="br0">)</span> <span class="kw4">const</span> B<span class="sy4">;</span> <span class="co1">// 未定义行为:试图重用 const 对象</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>一旦在某个对象所曾占据的地址上创建了新的对象,所有原对象的指针、引用及名字都会自动代表新的对象,而且一旦新对象的生存期开始,它们就可以用于操作这个新对象,但仅当新对象可透明替换旧对象才可以。</p>
|
|
<p>对象 <code>y</code> <i>可透明替换</i>对象 <code>x</code> ,若:</p>
|
|
<ul>
|
|
<li><code>y</code> 的存储准确地与 <code>x</code> 所占据的存储位置重叠</li>
|
|
<li><code>y</code> 与 <code>x</code> 拥有相同类型(忽略顶层 cv 限定符)</li>
|
|
<li><code>x</code> 不是完整 const 对象</li>
|
|
<li><code>x</code> 与 <code>y</code> 均不是基类子对象<span class="t-rev-inl t-since-cxx20"><span>,或以 <code>[[<a href="cpp-language-attributes-no_unique_address.html">no_unique_address</a>]]</code> 声明的成员子对象</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span></li>
|
|
<li>要么</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li><code>x</code> 与 <code>y</code> 都是完整对象,或</li>
|
|
<li><code>x</code> 与 <code>y</code> 分别为对象 <code>ox</code> 与 <code>oy</code> 的直接子对象,而 <code>oy</code> 可透明替换 <code>ox</code>。</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> C <span class="br0">{</span>
|
|
<span class="kw4">int</span> i<span class="sy4">;</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw4">const</span> C<span class="sy3">&</span> operator<span class="sy1">=</span><span class="br0">(</span> <span class="kw4">const</span> C<span class="sy3">&</span> <span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">const</span> C<span class="sy3">&</span> C<span class="sy4">::</span><span class="me2">operator</span><span class="sy1">=</span><span class="br0">(</span> <span class="kw4">const</span> C<span class="sy3">&</span> other<span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="kw1">if</span> <span class="br0">(</span> this <span class="sy3">!</span><span class="sy1">=</span> <span class="sy3">&</span>other <span class="br0">)</span> <span class="br0">{</span>
|
|
this<span class="sy2">-</span><span class="sy1">></span>~C<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// *this 的生存期结束</span>
|
|
new <span class="br0">(</span>this<span class="br0">)</span> C<span class="br0">(</span>other<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 创建了 C 类型的新对象</span>
|
|
f<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 定义明确的</span>
|
|
<span class="br0">}</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>this<span class="sy4">;</span>
|
|
<span class="br0">}</span>
|
|
C c1<span class="sy4">;</span>
|
|
C c2<span class="sy4">;</span>
|
|
c1 <span class="sy1">=</span> c2<span class="sy4">;</span> <span class="co1">// 定义明确的</span>
|
|
c1.<span class="me1">f</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 定义明确的;c1 代表 C 类型的一个新对象</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>如果未能满足以上所列出的各项条件的话,还可以通过采用指针优化屏障 <span class="t-lc"><a href="cpp-utility-launder.html">std::launder</a></span> 来获得指向新对象的有效指针:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> A <span class="br0">{</span>
|
|
<span class="kw1">virtual</span> <span class="kw4">int</span> transmogrify<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> B <span class="sy4">:</span> A <span class="br0">{</span>
|
|
<span class="kw4">int</span> transmogrify<span class="br0">(</span><span class="br0">)</span> override <span class="br0">{</span> <span class="sy4">::</span><span class="me2">new</span><span class="br0">(</span>this<span class="br0">)</span> A<span class="sy4">;</span> <span class="kw1">return</span> <span class="nu0">2</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">inline</span> <span class="kw4">int</span> A<span class="sy4">::</span><span class="me2">transmogrify</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="sy4">::</span><span class="me2">new</span><span class="br0">(</span>this<span class="br0">)</span> B<span class="sy4">;</span> <span class="kw1">return</span> <span class="nu0">1</span><span class="sy4">;</span> <span class="br0">}</span>
|
|
|
|
<span class="kw4">void</span> test<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
A i<span class="sy4">;</span>
|
|
<span class="kw4">int</span> n <span class="sy1">=</span> i.<span class="me1">transmogrify</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="co1">// int m = i.transmogrify(); // 未定义行为:新的 A 对象是基类子对象,而旧的是完整对象</span>
|
|
<span class="kw4">int</span> m <span class="sy1">=</span> <a href="cpp-utility-launder.html"><span class="kw1122">std::<span class="me2">launder</span></span></a><span class="br0">(</span><span class="sy3">&</span>i<span class="br0">)</span><span class="sy2">-</span><span class="sy1">></span>transmogrify<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>m <span class="sy2">+</span> n <span class="sy1">==</span> <span class="nu0">3</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>相似地,当在类成员或数组元素的存储中创建对象时,只有满足如下条件,所创建的对象才是包含原对象的对象的子对象(成员或元素):</p>
|
|
<ul>
|
|
<li>包含对象的生存期已经开始且尚未结束</li>
|
|
<li>新对象的存储与原对象的存储严格重合</li>
|
|
<li>新对象和原对象(忽略 cv 限定性)具有相同的类型。</li>
|
|
</ul>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<p>否则不使用 <span class="t-lc"><a href="cpp-utility-launder.html">std::launder</a></span> 就不能以原对象的名字访问新对象:</p>
|
|
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style="">
|
|
<tr>
|
|
<td class="mbox-empty-cell"></td>
|
|
<td class="mbox-text" style="">本节未完成<br />
|
|
原因:合适的示例</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>一种特殊情况是,满足以下条件的情况下可以在含有 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> <span class="t-rev-inl t-since-cxx17"><span>或 <a href="cpp-types-byte.html"><tt>std::byte</tt></a></span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span> 的数组中创建对象(这种情况下称这个数组为对象<i>提供存储</i>):</p>
|
|
<ul>
|
|
<li>数组的生存期已经开始且尚未结束</li>
|
|
<li>新对象的存储完全适于数组之内</li>
|
|
<li>不存在满足这些约束的,内嵌于该数组的数组对象。</li>
|
|
</ul>
|
|
<p>如果该数组的这个部分之前曾为另一个对象提供存储,那个对象的生存期就会因为其存储被重用而结束,不过数组自身的生存期并未结束(其存储并不被当成是被重用了)。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">template</span><span class="sy1"><</span><span class="kw1">typename</span> ...<span class="me1">T</span><span class="sy1">></span>
|
|
<span class="kw1">struct</span> AlignedUnion <span class="br0">{</span>
|
|
alignas<span class="br0">(</span>T...<span class="br0">)</span> <span class="kw4">unsigned</span> <span class="kw4">char</span> data<span class="br0">[</span>maxv<span class="br0">(</span>sizeof<span class="br0">(</span>T<span class="br0">)</span>...<span class="br0">)</span><span class="br0">]</span><span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">int</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
AlignedUnion<span class="sy1"><</span><span class="kw4">int</span>, <span class="kw4">char</span><span class="sy1">></span> au<span class="sy4">;</span>
|
|
<span class="kw4">int</span> <span class="sy2">*</span>p <span class="sy1">=</span> new <span class="br0">(</span>au.<span class="me1">data</span><span class="br0">)</span> <span class="kw4">int</span><span class="sy4">;</span> <span class="co1">// OK : au.data 提供存储</span>
|
|
<span class="kw4">char</span> <span class="sy2">*</span>c <span class="sy1">=</span> new <span class="br0">(</span>au.<span class="me1">data</span><span class="br0">)</span> <span class="kw4">char</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK : *p 的生存期结束</span>
|
|
<span class="kw4">char</span> <span class="sy2">*</span>d <span class="sy1">=</span> new <span class="br0">(</span>au.<span class="me1">data</span> <span class="sy2">+</span> <span class="nu0">1</span><span class="br0">)</span> <span class="kw4">char</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="kw1">return</span> <span class="sy2">*</span>c <span class="sy2">+</span> <span class="sy2">*</span>d<span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.9C.A8.E7.94.9F.E5.AD.98.E6.9C.9F.E4.B9.8B.E5.A4.96.E8.BF.9B.E8.A1.8C.E8.AE.BF.E9.97.AE">在生存期之外进行访问</span></h3>
|
|
<p>在对象的生存期开始之前但其存储将要占据的存储已经分配之后,或者在对象的生存期已经结束之后但其所曾占据的存储被重用或释放之前,对代表这个对象的泛左值表达式的以下这些用法是未定义的:</p>
|
|
<ol>
|
|
<li>左值向右值转换(比如对接受其值的函数进行调用),除非正在构造或析构该对象(此时应用不同的规则集合):</li>
|
|
<li>访问其非静态数据成员或调用非静态成员函数。</li>
|
|
<li>绑定引用到其某个虚基类子对象。</li>
|
|
<li><a href="cpp-language-dynamic_cast.html"><code>dynamic_cast</code></a> 或 <a href="cpp-language-typeid.html"><code>typeid</code></a> 表达式。</li>
|
|
</ol>
|
|
<p>以上规则也适用于指针(绑定引用到虚基类改为隐式转换为虚基类的指针),并有两条额外的规则:</p>
|
|
<ol>
|
|
<li>对指向没有对象的存储的指针进行 <a href="cpp-language-static_cast.html"><code>static_cast</code></a> 时只允许将其强制转换为(可能 cv 限定的)<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span><span class="sy2">*</span></span></span>。</li>
|
|
<li>转型到 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span><span class="sy2">*</span></span></span> 的指向无对象存储的指针,只能被 <a href="cpp-language-static_cast.html"><code>static_cast</code></a> 到指向可能 cv 限定的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span> 、或可能 cv 限定的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> <span class="t-rev-inl t-since-cxx17"><span>、或可能 cv 限定的 <span class="t-lc">std::byte</span></span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span> 的指针。</li>
|
|
</ol>
|
|
<p>在构造和析构的过程中,通常允许调用非静态成员函数,访问非静态数据成员,以及使用 <a href="cpp-language-typeid.html"><code>typeid</code></a> 和 <a href="cpp-language-dynamic_cast.html"><code>dynamic_cast</code></a>。然而,因为生存期尚未开始(在构造期间)或已结束(在析构期间),只允许特定的操作。其他限制条件参见<a href="cpp-language-virtual.html#.E5.9C.A8.E6.9E.84.E9.80.A0.E4.B8.8E.E6.9E.90.E6.9E.84.E6.9C.9F.E9.97.B4">在构造和析构过程中调用虚函数</a>。</p>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<p><a rel="nofollow" class="external text" href="https://wg21.link/p1358r0#2256">核心问题 2256</a> 解决前,非类对象(存储期的终止)和类对象(按构造顺序的逆序)的生存期终止规则存在差别:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> A <span class="br0">{</span>
|
|
<span class="kw4">int</span><span class="sy2">*</span> p<span class="sy4">;</span>
|
|
~A<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="sy2">*</span>p<span class="sy4">;</span> <span class="br0">}</span> <span class="co1">// CWG2256 起为未定义行为: n 不活到 a 的生存期之后</span>
|
|
<span class="co1">// CWG2256 前有恰当定义:打印 123</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
A a<span class="sy4">;</span>
|
|
<span class="kw4">int</span> n <span class="sy1">=</span> <span class="nu0">123</span><span class="sy4">;</span> <span class="co1">// 假如 n 不活到 a 的生存期之后,则能把这条语句优化掉(死存储)</span>
|
|
a.<span class="me1">p</span> <span class="sy1">=</span> <span class="sy3">&</span>n<span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p><a rel="nofollow" class="external text" href="https://wg21.link/p1971r0#RU007">RU007</a> 解决前, const 限定类型或引用类型的非静态数据成员使含有它的对象无法为可透明替换,这使得 <span class="t-lc"><a href="cpp-container-vector.html">std::vector</a></span> 与 <span class="t-lc"><a href="cpp-container-deque.html">std::deque</a></span> 难以实现:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw1">struct</span> X <span class="br0">{</span> <span class="kw4">const</span> <span class="kw4">int</span> n<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">union</span> U <span class="br0">{</span> X x<span class="sy4">;</span> <span class="kw4">float</span> f<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw4">void</span> tong<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
|
|
U u <span class="sy1">=</span> <span class="br0">{</span> <span class="br0">{</span> <span class="nu0">1</span> <span class="br0">}</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
u.<span class="me1">f</span> <span class="sy1">=</span> <span class="nu0">5</span>.<span class="me1">f</span><span class="sy4">;</span> <span class="co1">// OK :创建 'u' 的新子对象</span>
|
|
X <span class="sy2">*</span>p <span class="sy1">=</span> new <span class="br0">(</span><span class="sy3">&</span>u.<span class="me1">x</span><span class="br0">)</span> X <span class="br0">{</span><span class="nu0">2</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// OK :创建 'u' 的新子对象</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>p<span class="sy2">-</span><span class="sy1">></span>n <span class="sy1">==</span> <span class="nu0">2</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// OK</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>u.<span class="me1">x</span>.<span class="me1">n</span> <span class="sy1">==</span> <span class="nu0">2</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// RU007 前未定义:</span>
|
|
<span class="co1">// 'u.x' 不指名新子对象</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span><span class="sy2">*</span><a href="cpp-utility-launder.html"><span class="kw1122">std::<span class="me2">launder</span></span></a><span class="br0">(</span><span class="sy3">&</span>u.<span class="me1">x</span>.<span class="me1">n</span><span class="br0">)</span> <span class="sy1">==</span> <span class="nu0">2</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 即使在 RU007 前也 OK</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E7.BC.BA.E9.99.B7.E6.8A.A5.E5.91.8A">缺陷报告</span></h3>
|
|
<p>下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。</p>
|
|
<table class="dsctable" style="font-size:0.8em">
|
|
<tr>
|
|
<th>DR</th>
|
|
<th>应用于</th>
|
|
<th>出版时的行为</th>
|
|
<th>正确行为</th>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue201">CWG 201</a></td>
|
|
<td>C++98</td>
|
|
<td>要求默认构造函数的默认实参中的临时对象的生存期在数组初始化完成时结束</td>
|
|
<td>生存期在下个元素初始化之前结束</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue2012">CWG 2012</a></td>
|
|
<td>C++98</td>
|
|
<td>引用的生存期被指定为与存储期匹配,这要求 extern 引用在其初始化器运行前已存活</td>
|
|
<td>生存期始于初始化</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue2107">CWG 2107</a></td>
|
|
<td>C++98</td>
|
|
<td>CWG 124 的解决方案未应用到复制构造函数</td>
|
|
<td>已应用</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.link/P0137R1">P0137R1</a></td>
|
|
<td>C++98</td>
|
|
<td>在 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> 数组中创建对象重用其存储</td>
|
|
<td>其存储不被重用</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue2256">CWG 2256</a></td>
|
|
<td>C++98</td>
|
|
<td>可平凡析构对象的生存期与其他对象不一致</td>
|
|
<td>使之一致</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.link/P1971R0">P1971R0</a></td>
|
|
<td>C++98</td>
|
|
<td>const 限定类型或引用类型的非静态数据成员使包含它的对象不能为可透明替换</td>
|
|
<td>移除限制</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.link/P2103R0">P2103R0</a></td>
|
|
<td>C++98</td>
|
|
<td>可透明替换性不要求保持原结构</td>
|
|
<td>要求</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.link/P0593R6">P0593R6</a></td>
|
|
<td>C++98</td>
|
|
<td>伪析构函数调用无效果</td>
|
|
<td>它销毁对象</td>
|
|
</tr>
|
|
<tr>
|
|
<td><a rel="nofollow" class="external text" href="https://wg21.cmeerw.net/cwg/issue2470">CWG 2470</a></td>
|
|
<td>C++98</td>
|
|
<td>可以有多于一个数组为同一对象提供存储</td>
|
|
<td>仅一个提供</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h3>
|
|
<div class="t-ref-std-c++20">
|
|
<ul>
|
|
<li>C++20 标准(ISO/IEC 14882:2020):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>6.7.3 Object lifetime [basic.life]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>11.10.4 Construction and destruction [class.cdtor]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++17">
|
|
<ul>
|
|
<li>C++17 标准(ISO/IEC 14882:2017):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>6.8 Object lifetime [basic.life]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>15.7 Construction and destruction [class.cdtor]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++14">
|
|
<ul>
|
|
<li>C++14 标准(ISO/IEC 14882:2014):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3 Object lifetime [basic.life]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>12.7 Construction and destruction [class.cdtor]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++11">
|
|
<ul>
|
|
<li>C++11 标准(ISO/IEC 14882:2011):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.8 Object lifetime [basic.life]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>12.7 Construction and destruction [class.cdtor]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++03">
|
|
<ul>
|
|
<li>C++03 标准(ISO/IEC 14882:2003):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.8 Object lifetime [basic.life]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>12.7 Construction and destruction [class.cdtor]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="t-ref-std-c++98">
|
|
<ul>
|
|
<li>C++98 标准(ISO/IEC 14882:1998):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>3.8 Object lifetime [basic.life]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>12.7 Construction and destruction [class.cdtor]</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
|
<table class="t-dsc-begin"></table>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|