288 lines
41 KiB
HTML
288 lines
41 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_object 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>C++ 程序可以创建、销毁、引用、访问并操作<i>对象</i>。</p>
|
|
<p>在 C++ 中,一个对象拥有这些性质:</p>
|
|
<ul>
|
|
<li>大小(可以使用 <a href="cpp-language-sizeof.html"><code>sizeof</code></a> 获取);</li>
|
|
<li>对齐要求(可以使用 <a href="cpp-language-alignof.html"><code>alignof</code></a> 获取);</li>
|
|
<li><a href="cpp-language-storage_duration.html">存储期</a>(自动、静态、动态、线程局部);</li>
|
|
<li><a href="cpp-language-lifetime.html">生存期</a>(与存储期绑定或者临时)</li>
|
|
<li><a href="cpp-language-type.html">类型</a>;</li>
|
|
<li>值(可能是不确定的,例如<a href="cpp-language-default_initialization.html">默认初始化</a>的非类类型);</li>
|
|
<li><a href="cpp-language-identifiers.html#.E5.90.8D.E5.AD.97">名字</a>(可选)。</li>
|
|
</ul>
|
|
<p>以下实体都不是对象:值,引用,函数,枚举项,类型,类的非静态成员,模板,类或函数模板的特化,命名空间,形参包,和 <span class="t-c"><span class="mw-geshi cpp source-cpp">this</span></span>。</p>
|
|
<p><i>变量</i>由<a href="cpp-language-declarations.html">声明</a>所引入,是一个对象或不是对非静态数据成员的引用。</p>
|
|
<h3><span class="mw-headline" id=".E5.AF.B9.E8.B1.A1.E5.88.9B.E5.BB.BA">对象创建</span></h3>
|
|
<p>对象能由<a href="cpp-language-definition.html">定义</a>、<a href="cpp-language-new.html">new 表达式</a>、<a href="cpp-language-throw.html">throw 表达式</a>、更改<a href="cpp-language-union.html">联合体</a>的活跃成员和求值要求<a href="cpp-language-lifetime.html#.E4.B8.B4.E6.97.B6.E5.AF.B9.E8.B1.A1.E7.94.9F.E5.AD.98.E6.9C.9F">临时对象</a>的表达式显式创建。显式对象创建中创建的对象是唯一定义的。</p>
|
|
<p><a href="cpp-language-lifetime.html#.E9.9A.90.E5.BC.8F.E7.94.9F.E5.AD.98.E6.9C.9F.E7.B1.BB.E5.9E.8B">隐式生存期类型</a>的对象也可以由下列操作隐式创建:</p>
|
|
<ul>
|
|
<li>开始 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span>、<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>或 <span class="t-lc">std::byte</span></span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>数组生存期的操作,该情况下在该数组中创建这种对象,</li>
|
|
<li>调用下列分配函数,该情况下在分配的存储中创建这种对象:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li><span class="t-lc"><a href="cpp-memory-new-operator_new.html">operator new</a></span></li>
|
|
<li><span class="t-lc"><a href="cpp-memory-new-operator_new.html">operator new[]</a></span></li>
|
|
<li><span class="t-lc"><a href="cpp-memory-c-malloc.html">std::malloc</a></span></li>
|
|
<li><span class="t-lc"><a href="cpp-memory-c-calloc.html">std::calloc</a></span></li>
|
|
<li><span class="t-lc"><a href="cpp-memory-c-realloc.html">std::realloc</a></span></li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx17">
|
|
<td>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li><span class="t-lc"><a href="cpp-memory-c-aligned_alloc.html">std::aligned_alloc</a></span></li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<ul>
|
|
<li>调用下列对象表示复制函数,该情况下在目标存储区域或结果中创建这种对象:</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li><span class="t-lc"><a href="cpp-string-byte-memcpy.html">std::memcpy</a></span></li>
|
|
<li><span class="t-lc"><a href="cpp-string-byte-memmove.html">std::memmove</a></span></li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx20">
|
|
<td>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li><span class="t-lc">std::bit_cast</span></li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>只要在能给予程序有定义的行为时,同一存储区域中可创建零或多个对象。如果无法这样创建,例如操作冲突,则程序行为未定义。若多个这种隐式创建的对象的集合会给予程序有定义行为,则不指定这些集合中的哪一个被创建。换言之,不要求隐式创建的对象是唯一定义的。</p>
|
|
<p>在指定的存储区域内隐式创建对象后,一些操作会生成指向<i>适合的已创建对象</i>的指针。适合的已创建对象与存储区域拥有相同地址。类似地,当且仅当不存在能给予程序有定义行为的指针值时,行为才未定义;而若有多个给予程序有定义行为的值,则不指定产生哪个值。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#include <cstdlib></span>
|
|
<span class="kw1">struct</span> X <span class="br0">{</span> <span class="kw4">int</span> a, b<span class="sy4">;</span> <span class="br0">}</span><span class="sy4">;</span>
|
|
X <span class="sy2">*</span>MakeX<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
<span class="co1">// 可能的有定义行为之一:</span>
|
|
<span class="co1">// 调用 std::malloc 隐式创建一个 X 类型对象及其子对象 a 与 b ,并返回指向该 X 对象的指针</span>
|
|
X <span class="sy2">*</span>p <span class="sy1">=</span> <span class="kw1">static_cast</span><span class="sy1"><</span>X<span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span><a href="cpp-memory-c-malloc.html"><span class="kw759">std::<span class="me2">malloc</span></span></a><span class="br0">(</span>sizeof<span class="br0">(</span>X<span class="br0">)</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
p<span class="sy2">-</span><span class="sy1">></span>a <span class="sy1">=</span> <span class="nu0">1</span><span class="sy4">;</span>
|
|
p<span class="sy2">-</span><span class="sy1">></span>b <span class="sy1">=</span> <span class="nu0">2</span><span class="sy4">;</span>
|
|
<span class="kw1">return</span> p<span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>调用 <span class="t-lc"><a href="cpp-memory-allocator-allocate.html">std::allocator::allocate</a></span> 或<a href="cpp-language-union.html">联合体</a>类型的隐式定义的复制/移动特殊成员函数亦能创建对象。</p>
|
|
<h3><span class="mw-headline" id=".E5.AF.B9.E8.B1.A1.E8.A1.A8.E7.A4.BA.E4.B8.8E.E5.80.BC.E8.A1.A8.E7.A4.BA">对象表示与值表示</span></h3>
|
|
<p>对于一个 <code>T</code> 类型的对象,其<i>对象表示 (object representation)</i> 是和它开始于同一个地址,且长度为 <span class="t-c"><span class="mw-geshi cpp source-cpp">sizeof<span class="br0">(</span>T<span class="br0">)</span></span></span> 的一段 <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>(或等价的 <span class="t-lc">std::byte</span>)</span> <span><span class="t-mark-rev t-since-cxx17">(C++17 起)</span></span></span>类型的对象序列。</p>
|
|
<p>对象的<i>值表示 (value representation)</i>则是用于持有它的类型 <code>T</code> 的值的位的集合。</p>
|
|
<p>对于<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 中会产生一个硬件异常,就像浮点值的 SNaN(“Signaling NaN 发信非数”)或整数值的 NaT(“Not a Thing 非事物”))。</p>
|
|
<p>反过来不一定是对的:<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> 类型的两个具有不同对象表示的对象可能表现出相同的值。例如,浮点数有多种位模式都表示相同的特殊值 <a href="cpp-numeric-math-NAN.html">NaN</a> 。更常见的是,对象表示的一些位可能根本不参与值表示;这些位可能是为了满足<a href="cpp-language-object.html#.E5.AF.B9.E9.BD.90">对齐要求</a>,<a href="cpp-language-bit_field.html">位域</a>的大小等得以满足而填充其间的。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#include <cassert></span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="kw4">char</span> c<span class="sy4">;</span> <span class="co1">// 1 字节值</span>
|
|
<span class="co1">// 3 字节填充(假设 alignof(float) == 4 )</span>
|
|
<span class="kw4">float</span> f<span class="sy4">;</span> <span class="co1">// 4 字节值 (假设 sizeof(float) == 4 )</span>
|
|
<span class="kw4">bool</span> operator<span class="sy1">==</span><span class="br0">(</span><span class="kw4">const</span> S<span class="sy3">&</span> arg<span class="br0">)</span> <span class="kw4">const</span> <span class="br0">{</span> <span class="co1">// 基于值的相等</span>
|
|
<span class="kw1">return</span> c <span class="sy1">==</span> arg.<span class="me1">c</span> <span class="sy3">&&</span> f <span class="sy1">==</span> arg.<span class="me1">f</span><span class="sy4">;</span>
|
|
<span class="br0">}</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>
|
|
static_assert<span class="br0">(</span>sizeof<span class="br0">(</span>S<span class="br0">)</span> <span class="sy1">==</span> <span class="nu0">8</span><span class="br0">)</span><span class="sy4">;</span>
|
|
S s1 <span class="sy1">=</span> <span class="br0">{</span><span class="st0">'a'</span>, <span class="nu16">3.14</span><span class="br0">}</span><span class="sy4">;</span>
|
|
S s2 <span class="sy1">=</span> s1<span class="sy4">;</span>
|
|
<span class="kw1">reinterpret_cast</span><span class="sy1"><</span><span class="kw4">unsigned</span> <span class="kw4">char</span><span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span><span class="sy3">&</span>s1<span class="br0">)</span><span class="br0">[</span><span class="nu0">2</span><span class="br0">]</span> <span class="sy1">=</span> <span class="st0">'b'</span><span class="sy4">;</span> <span class="co1">// 更改填充的第 2 字节</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>s1 <span class="sy1">==</span> s2<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 值并未更改</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>对于 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span>,<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">signed</span> <span class="kw4">char</span></span></span>,和 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> 类型的对象,除非它们是大小过大的<a href="cpp-language-bit_field.html">位域</a>,否则其对象表示的每个位都参与其值表示,而且每一种位模式都表示一个独立的值(没有填充位或陷阱位,不允许值的多种表示)。</p>
|
|
<h3><span class="mw-headline" id=".E5.AD.90.E5.AF.B9.E8.B1.A1">子对象</span></h3>
|
|
<p>一个对象可以拥有<i>子对象</i>。子对象包括:</p>
|
|
<ul>
|
|
<li>成员对象</li>
|
|
<li>基类子对象</li>
|
|
<li>数组元素</li>
|
|
</ul>
|
|
<p>不是其他任何对象的子对象的对象称为<i>完整对象</i>。</p>
|
|
<p>若一个子对象是基类子对象<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>,则它<i>潜在重叠</i>。</p>
|
|
<p>完整对象、成员对象和数组元素也被称为<i>最终派生对象</i> ,以便和基类子对象区分开。既非潜在重叠亦非<a href="cpp-language-bit_field.html">位域</a>的对象的大小不能为零(基类子对象的大小可能为零<span class="t-rev-inl t-since-cxx20"><span>,即使无 <code>[[no_unique_address]]</code> 也是如此</span> <span><span class="t-mark-rev t-since-cxx20">(C++20 起)</span></span></span>:参见<a href="cpp-language-ebo.html">空基类优化</a>)。</p>
|
|
<p>一个对象能含有其他对象,该情况下被含有的对象<i>内嵌于</i>前述对象。若符合下列条件,则对象 <code>a</code> 内嵌于另一对象 <code>b</code> :</p>
|
|
<ul>
|
|
<li><code>a</code> 是 <code>b</code> 的子对象,或</li>
|
|
<li><code>b</code> 为 <code>a</code> 提供存储,或</li>
|
|
<li>存在对象 <code>c</code> ,其中 <code>a</code> 内嵌于 <code>c</code> 而 <code>c</code> 内嵌于 <code>b</code> 。</li>
|
|
</ul>
|
|
<p>任何两个具有交叠的<a href="cpp-language-lifetime.html">生存期</a>的(非<a href="cpp-language-bit_field.html">位域</a>)对象必然有不同的地址,除非其中一个对象内嵌于另一个对象,或者两个对象都是同一个完整对象中的不同类型的子对象,且其中一个是大小为零的子对象。</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="kw4">static</span> <span class="kw4">const</span> <span class="kw4">char</span> c1 <span class="sy1">=</span> <span class="st0">'x'</span><span class="sy4">;</span>
|
|
<span class="kw4">static</span> <span class="kw4">const</span> <span class="kw4">char</span> c2 <span class="sy1">=</span> <span class="st0">'x'</span><span class="sy4">;</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span><span class="sy3">&</span>c1 <span class="sy3">!</span><span class="sy1">=</span> <span class="sy3">&</span>c2<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 值相同,地址不同</span></pre></div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.A4.9A.E6.80.81.E5.AF.B9.E8.B1.A1">多态对象</span></h3>
|
|
<p>声明或继承了至少一个虚函数的类类型的对象是多态对象。每个多态对象中,实现都会储存额外的信息(在所有现存的实现中,如果没被编译器优化掉的话,这就是一个指针),它被用于进行<a href="cpp-language-virtual.html">虚函数</a>的调用,RTTI 功能特性(<a href="cpp-language-dynamic_cast.html"><code>dynamic_cast</code></a> 和 <a href="cpp-language-typeid.html"><code>typeid</code></a>)也用它在运行时确定对象创建时所用的类型,而不管使用它的表达式是什么类型。</p>
|
|
<p>对于非多态对象,值的解释方式由使用对象的表达式所确定,这在编译期就已经决定了。</p>
|
|
<div class="t-example">
|
|
<div class="t-example-live-link"></div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#include <iostream></span>
|
|
<span class="co2">#include <typeinfo></span>
|
|
<span class="kw1">struct</span> Base1 <span class="br0">{</span>
|
|
<span class="co1">// 多态类型:声明了虚成员</span>
|
|
<span class="kw1">virtual</span> ~Base1<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> Derived1 <span class="sy4">:</span> Base1 <span class="br0">{</span>
|
|
<span class="co1">// 多态类型:继承了虚成员</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> Base2 <span class="br0">{</span>
|
|
<span class="co1">// 非多态类型</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> Derived2 <span class="sy4">:</span> Base2 <span class="br0">{</span>
|
|
<span class="co1">// 非多态类型</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
Derived1 obj1<span class="sy4">;</span> <span class="co1">// object1 创建为类型 Derived1</span>
|
|
Derived2 obj2<span class="sy4">;</span> <span class="co1">// object2 创建为类型 Derived2</span>
|
|
|
|
Base1<span class="sy3">&</span> b1 <span class="sy1">=</span> obj1<span class="sy4">;</span> <span class="co1">// b1 指代对象 obj1</span>
|
|
Base2<span class="sy3">&</span> b2 <span class="sy1">=</span> obj2<span class="sy4">;</span> <span class="co1">// b2 指代对象 obj2</span>
|
|
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"b1 的表达式类型: "</span> <span class="sy1"><<</span> <span class="kw1">typeid</span><span class="br0">(</span>decltype<span class="br0">(</span>b1<span class="br0">)</span><span class="br0">)</span>.<span class="me1">name</span><span class="br0">(</span><span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span>
|
|
<span class="sy1"><<</span> <span class="st0">"b2 的表达式类型: "</span> <span class="sy1"><<</span> <span class="kw1">typeid</span><span class="br0">(</span>decltype<span class="br0">(</span>b2<span class="br0">)</span><span class="br0">)</span>.<span class="me1">name</span><span class="br0">(</span><span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span>
|
|
<span class="sy1"><<</span> <span class="st0">"b1 的对象类型: "</span> <span class="sy1"><<</span> <span class="kw1">typeid</span><span class="br0">(</span>b1<span class="br0">)</span>.<span class="me1">name</span><span class="br0">(</span><span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span>
|
|
<span class="sy1"><<</span> <span class="st0">"b2 的对象类型: "</span> <span class="sy1"><<</span> <span class="kw1">typeid</span><span class="br0">(</span>b2<span class="br0">)</span>.<span class="me1">name</span><span class="br0">(</span><span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span>
|
|
<span class="sy1"><<</span> <span class="st0">"b1 的大小: "</span> <span class="sy1"><<</span> sizeof b1 <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span>
|
|
<span class="sy1"><<</span> <span class="st0">"b2 的大小: "</span> <span class="sy1"><<</span> sizeof b2 <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>可能的输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">b1 的表达式类型: Base1
|
|
b2 的表达式类型: Base2
|
|
b1 的对象类型: Derived1
|
|
b2 的对象类型: Base2
|
|
b1 的大小: 8
|
|
b2 的大小: 1</pre></div>
|
|
</div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E4.B8.A5.E6.A0.BC.E7.9A.84.E5.88.AB.E5.90.8D.E4.BD.BF.E7.94.A8">严格的别名使用</span></h3>
|
|
<p>在很多情况下,通过类型与对象的创建类型不同的表达式来访问对象都是未定义行为,其例子和例外请参考 <a href="cpp-language-reinterpret_cast.html#.E7.B1.BB.E5.9E.8B.E5.88.AB.E5.90.8D.E4.BD.BF.E7.94.A8">reinterpret_cast</a>。</p>
|
|
<h3><span class="mw-headline" id=".E5.AF.B9.E9.BD.90">对齐</span></h3>
|
|
<p>每个<a href="cpp-language-type.html">对象类型</a>都具有被称为<i>对齐要求(alignment requirement)</i>的性质,它是一个整数(类型为 <span class="t-lc"><a href="cpp-types-size_t.html">std::size_t</a></span>,总是 2 的幂),表示这个类型的不同对象所能分配放置的连续相邻地址之间的字节数。</p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>可以用 <a href="cpp-language-alignof.html"><code>alignof</code></a> 或 <span class="t-lc"><a href="cpp-types-alignment_of.html">std::alignment_of</a></span> 来查询类型的对齐要求。可以使用指针对齐函数 <span class="t-lc"><a href="cpp-memory-align.html">std::align</a></span> 来获取某个缓冲区中经过适当对齐的指针,还可以使用 <span class="t-lc"><a href="cpp-types-aligned_storage.html">std::aligned_storage</a></span> 来获取经过适当对齐的存储区。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<p>每个对象类型在该类型的所有对象上强制该类型的对齐要求<span class="t-rev-inl t-since-cxx11"><span>;可以使用 <a href="cpp-language-alignas.html"><code>alignas</code></a> 来要求更严格的对齐(更大的对齐要求)</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>。</p>
|
|
<p>为了使<a href="cpp-language-class.html">类</a>中的所有非静态成员都符合对齐要求,会在一些成员后面插入一些<i>填充</i>。</p>
|
|
<div class="t-example">
|
|
<div class="t-example-live-link"></div>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="cpp source-cpp">
|
|
<pre class="de1"><span class="co2">#include <iostream></span>
|
|
|
|
<span class="co1">// S 类型的对象可以在任何地址上分配</span>
|
|
<span class="co1">// 因为 S.a 和 S.b 都可以在任何地址上分配</span>
|
|
<span class="kw1">struct</span> S <span class="br0">{</span>
|
|
<span class="kw4">char</span> a<span class="sy4">;</span> <span class="co1">// 大小:1,对齐:1</span>
|
|
<span class="kw4">char</span> b<span class="sy4">;</span> <span class="co1">// 大小:1,对齐:1</span>
|
|
<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 大小:2,对齐:1</span>
|
|
|
|
<span class="co1">// X 类型的对象只能在 4 字节边界上分配</span>
|
|
<span class="co1">// 因为 X.n 必须在 4 字节边界上分配</span>
|
|
<span class="co1">// 因为 int 的对齐要求(通常)就是 4</span>
|
|
<span class="kw1">struct</span> X <span class="br0">{</span>
|
|
<span class="kw4">int</span> n<span class="sy4">;</span> <span class="co1">// 大小:4,对齐:4</span>
|
|
<span class="kw4">char</span> c<span class="sy4">;</span> <span class="co1">// 大小:1,对齐:1</span>
|
|
<span class="co1">// 三个填充字节</span>
|
|
<span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 大小:8,对齐:4</span>
|
|
|
|
<span class="kw4">int</span> main<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="st0">"sizeof(S) = "</span> <span class="sy1"><<</span> sizeof<span class="br0">(</span>S<span class="br0">)</span>
|
|
<span class="sy1"><<</span> <span class="st0">" alignof(S) = "</span> <span class="sy1"><<</span> alignof<span class="br0">(</span>S<span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span>
|
|
<a href="cpp-io-cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1"><<</span> <span class="st0">"sizeof(X) = "</span> <span class="sy1"><<</span> sizeof<span class="br0">(</span>X<span class="br0">)</span>
|
|
<span class="sy1"><<</span> <span class="st0">" alignof(X) = "</span> <span class="sy1"><<</span> alignof<span class="br0">(</span>X<span class="br0">)</span> <span class="sy1"><<</span> <span class="st0">'<span class="es1">\n</span>'</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>可能的输出:</p>
|
|
<div dir="ltr" class="mw-geshi" style="text-align: left;">
|
|
<div class="text source-text">
|
|
<pre class="de1">sizeof(S) = 2 alignof(S) = 1
|
|
sizeof(X) = 8 alignof(X) = 4</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>最弱的对齐(最小的对齐要求)是 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">char</span></span></span>、<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">signed</span> <span class="kw4">char</span></span></span> 和 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">unsigned</span> <span class="kw4">char</span></span></span> 的对齐,等于 1 ;所有类型中最大的<i>基础对齐(fundamental alignment)</i>是实现定义的<span class="t-rev-inl t-since-cxx11"><span>,并等于 <span class="t-lc"><a href="cpp-types-max_align_t.html">std::max_align_t</a></span> 的对齐。</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span></p>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p>当使用 <a href="cpp-language-alignas.html"><code>alignas</code></a> 使某个类型的对齐比 <span class="t-lc"><a href="cpp-types-max_align_t.html">std::max_align_t</a></span> 的更严格(更大)时,称其为具有<i>扩展对齐(extended alignment)</i>要求的类型。具有扩展对齐的类型或包含具有扩展对齐的非静态成员的类类型称为<i>过对齐(over-aligned)类型</i>。 <span class="t-rev-inl t-until-cxx17"><span><a href="cpp-language-new.html">new 表达式</a>、</span> <span><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></span></span><span class="t-lc"><a href="cpp-memory-allocator-allocate.html">std::allocator::allocate</a></span> <span class="t-rev-inl t-until-cxx20"><span>和 <span class="t-lc"><a href="cpp-memory-get_temporary_buffer.html">std::get_temporary_buffer</a></span></span> <span><span class="t-mark-rev t-until-cxx20">(C++20 前)</span></span></span> 是否支持过对齐类型是由实现定义的。以过对齐类型实例化的<a href="cpp-named_req-Allocator.html"><span style="font-style:italic">分配器</span> <span style="font-family: Georgia, 'DejaVu Serif', serif; font-style:italic">(Allocator)</span></a> 允许在编译期发生实例化失败,在运行时抛出 <span class="t-lc"><a href="cpp-memory-new-bad_alloc.html">std::bad_alloc</a></span> 异常,静默忽略不支持的对齐要求,也允许正确地处理它们。</p>
|
|
</td>
|
|
<td><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<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.link/P0593R6">P0593R6</a></td>
|
|
<td>C++98</td>
|
|
<td>先前的对象模型不支持标准库所要求的许多有用的手法且与 C 中的有效类型不兼容</td>
|
|
<td>添加了隐式对象创建</td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E5.8F.82.E8.80.83">参考</span></h3>
|
|
<table class="t-dsc-begin"></table>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|