179 lines
14 KiB
HTML
179 lines
14 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_ebo 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>允许空的基类子对象大小为零。</p>
|
|
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
|
<p>为保证同一类型的不同对象地址始终有别,要求任何<a href="cpp-language-object.html">对象</a>或成员子对象的大小至少为 1,即使该类型是空的<a href="cpp-language-class.html">类类型</a>(即没有非静态数据成员的 class 或 struct)<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>。</p>
|
|
<p>然而,基类子对象不受这种制约,而且可以完全从对象布局中被优化掉:</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> Base <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 空类</span>
|
|
|
|
<span class="kw1">struct</span> Derived1 <span class="sy4">:</span> Base <span class="br0">{</span>
|
|
<span class="kw4">int</span> i<span class="sy4">;</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>
|
|
<span class="co1">// 任何空类类型的对象大小至少为 1</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>sizeof<span class="br0">(</span>Base<span class="br0">)</span> <span class="sy1">></span> <span class="nu0">0</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 应用空基类优化</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>sizeof<span class="br0">(</span>Derived1<span class="br0">)</span> <span class="sy1">==</span> sizeof<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<p>如果首个非静态数据成员的类型与一个空基类的类型相同或者由该空基类派生,那么禁用空基类优化,因为要求两个同类型基类子对象在最终派生类型的对象表示中必须拥有不同地址。</p>
|
|
<p>这种情况的典例是 <span class="t-lc"><a href="cpp-iterator-reverse_iterator.html">std::reverse_iterator</a></span> 的朴素实现(派生自空基类 <span class="t-lc"><a href="cpp-iterator-iterator.html">std::iterator</a></span>),它持有其底层迭代器(同样派生自 <span class="t-lc"><a href="cpp-iterator-iterator.html">std::iterator</a></span>)为其首个非静态数据成员。</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> Base <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 空类</span>
|
|
|
|
<span class="kw1">struct</span> Derived1 <span class="sy4">:</span> Base <span class="br0">{</span>
|
|
<span class="kw4">int</span> i<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> Derived2 <span class="sy4">:</span> Base <span class="br0">{</span>
|
|
Base c<span class="sy4">;</span> <span class="co1">// Base,占用 1 字节,后随对 i 的填充</span>
|
|
<span class="kw4">int</span> i<span class="sy4">;</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
|
|
<span class="kw1">struct</span> Derived3 <span class="sy4">:</span> Base <span class="br0">{</span>
|
|
Derived1 c<span class="sy4">;</span> <span class="co1">// 从 Base 派生,占用 sizeof(int) 字节</span>
|
|
<span class="kw4">int</span> i<span class="sy4">;</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>
|
|
<span class="co1">// 不应用空基优化,</span>
|
|
<span class="co1">// 基类占用 1 字节,Base 成员占用 1 字节</span>
|
|
<span class="co1">// 后随 2 个填充字节以满足 int 的对齐要求</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>sizeof<span class="br0">(</span>Derived2<span class="br0">)</span> <span class="sy1">==</span> <span class="nu0">2</span><span class="sy2">*</span>sizeof<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 不应用空基类优化,</span>
|
|
<span class="co1">// 基类占用至少 1 字节加填充,</span>
|
|
<span class="co1">// 以满足首个成员的对齐要求(其对齐要求与 int 相同)</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>sizeof<span class="br0">(</span>Derived3<span class="br0">)</span> <span class="sy1">==</span> <span class="nu0">3</span><span class="sy2">*</span>sizeof<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
|
|
<span class="br0">}</span></pre></div>
|
|
</div>
|
|
<table class="t-rev-begin">
|
|
<tr class="t-rev t-since-cxx11">
|
|
<td>
|
|
<p><a href="cpp-named_req-StandardLayoutType.html"><span style="font-style:italic">标准布局类型</span> <span style="font-family: Georgia, 'DejaVu Serif', serif; font-style:italic">(StandardLayoutType)</span></a> <i>必须</i>应用空基类优化以维持指向标准布局对象的指针在用 <a href="cpp-language-reinterpret_cast.html">reinterpret_cast</a> 转换后还指向其首成员,这是标准要求标准布局类型“在同一个类中声明所有非静态数据成员(全在派生类或全在某个基类)”,并且“不能有与首个非静态数据成员类型相同的基类”的原因。</p>
|
|
</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-since-cxx20">
|
|
<td>
|
|
<p>如果空成员子对象使用属性 <code>[[<a href="cpp-language-attributes-no_unique_address.html">no_unique_address</a>]]</code>,那么允许像空基类一样优化掉它们。取这种成员的地址会产生可能等于同一个对象的某个其他成员的地址。</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> Empty <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="co1">// 空类</span>
|
|
|
|
<span class="kw1">struct</span> X <span class="br0">{</span>
|
|
<span class="kw4">int</span> i<span class="sy4">;</span>
|
|
<span class="br0">[</span><span class="br0">[</span>no_unique_address<span class="br0">]</span><span class="br0">]</span> Empty e<span class="sy4">;</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>
|
|
<span class="co1">// 任何空类类型对象的大小至少为 1</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>sizeof<span class="br0">(</span>Empty<span class="br0">)</span> <span class="sy1">>=</span> <span class="nu0">1</span><span class="br0">)</span><span class="sy4">;</span>
|
|
|
|
<span class="co1">// 空成员被优化掉</span>
|
|
<a href="cpp-error-assert.html"><span class="kw774">assert</span></a><span class="br0">(</span>sizeof<span class="br0">(</span>X<span class="br0">)</span> <span class="sy1">==</span> sizeof<span class="br0">(</span><span class="kw4">int</span><span class="br0">)</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-cxx20">(C++20 起)</span></td>
|
|
</tr>
|
|
</table>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<p>空基类优化常用于具分配器的标准库类(<span class="t-lc"><a href="cpp-container-vector.html">std::vector</a></span>、<span class="t-lc"><a href="cpp-utility-functional-function.html">std::function</a></span>、<span class="t-lc"><a href="cpp-memory-shared_ptr.html">std::shared_ptr</a></span> 等),使得当分配器无状态时可避免为其分配器成员占用任何额外存储。这是通过将必要的数据成员之一(例如 vector 的 begin、end 或 capacity 指针)与分配器一起,在 <a rel="nofollow" class="external text" href="http://www.boost.org/doc/libs/release/libs/utility/compressed_pair.htm">boost::compressed_pair</a> 的某种等价物中存储而实现的。</p>
|
|
<h3><span class="mw-headline" id=".E5.BC.95.E7.94.A8">引用</span></h3>
|
|
<div class="t-ref-std-c++11">
|
|
<ul>
|
|
<li>C++11 标准(ISO/IEC 14882:2011):</li>
|
|
</ul>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>5.10 Equality operators [expr.eq](p: 2)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>5.3.3 Sizeof [expr.sizeof](p: 2)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>9 Classes [class](p: 4,7)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>9.2 Class members [class.mem](p: 20)</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>5.10 Equality operators [expr.eq](p: 2)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>5.3.3 Sizeof [expr.sizeof](p: 2)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
<dl>
|
|
<dd>
|
|
<ul>
|
|
<li>9 Classes [class](p: 3)</li>
|
|
</ul>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|