145 lines
21 KiB
HTML
145 lines
21 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>dynamic_cast 转换</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_dynamic_cast skin-cppreference2 action-view cpp-navbar">
|
|
<div id="cpp-content-base">
|
|
<div id="content"><a id="top"></a>
|
|
<h1 id="firstHeading" class="firstHeading">dynamic_cast 转换</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.AF.AD.E6.B3.95">语法</span></h3>
|
|
<table class="t-sdsc-begin">
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
<tr class="t-sdsc">
|
|
<td class="t-sdsc-nopad"><code><b>dynamic_cast</b></code> <code><b><</b></code> <span class="t-spar">新类型</span> <code><b>></b></code> <code><b>(</b></code> <span class="t-spar">表达式</span> <code><b>)</b></code></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
<td class="t-sdsc-nopad"></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="10" class="t-sdsc-sep"></td>
|
|
</tr>
|
|
</table>
|
|
<table class="t-par-begin">
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">新类型</span></td>
|
|
<td>-</td>
|
|
<td>指向完整类类型的指针,到完整类类型的引用,或指向(可为 cv 限定的)void 的指针</td>
|
|
</tr>
|
|
<tr class="t-par">
|
|
<td><span class="t-spar">表达式</span></td>
|
|
<td>-</td>
|
|
<td>如果 <span class="t-spar">新类型</span> 为引用,则为完整类类型的<span class="t-rev-inl t-until-cxx11"><span>左值</span> <span><span class="t-mark-rev t-until-cxx11">(C++11 前)</span></span></span><span class="t-rev-inl t-since-cxx11"><span>泛左值</span> <span><span class="t-mark-rev t-since-cxx11">(C++11 起)</span></span></span>表达式,若 <span class="t-spar">新类型</span> 为指针,则为指向完整类类型的指针纯右值。</td>
|
|
</tr>
|
|
</table>
|
|
<p>如果转型成功,则 <code>dynamic_cast</code> 返回 <span class="t-spar">新类型</span> 类型的值。如果转型失败且 <span class="t-spar">新类型</span> 是指针类型,则它返回该类型的空指针。如果转型失败且 <span class="t-spar">新类型</span> 是引用类型,则它抛出与类型 <span class="t-lc"><a href="cpp-types-bad_cast.html">std::bad_cast</a></span> 的处理块匹配的异常。</p>
|
|
<h3><span class="mw-headline" id=".E8.A7.A3.E9.87.8A">解释</span></h3>
|
|
<p>只有下列转换在不去除<i>常量性</i>或<i>易变性</i>的场合才能用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">dynamic_cast</span></span></span> 进行。</p>
|
|
<div class="t-li1"><span class="t-li">1)</span> 如果 <span class="t-spar">表达式</span> 的类型刚好是 <span class="t-spar">新类型</span> 或 <span class="t-spar">新类型</span> 的更少 cv 限定版本,则结果是 <span class="t-spar">表达式</span> 具有 <span class="t-spar">新类型</span> 类型的值。(换言之,<span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">dynamic_cast</span></span></span> 可以用来添加常量性。隐式转换和 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">static_cast</span></span></span> 也能进行此转换。)</div>
|
|
<div class="t-li1"><span class="t-li">2)</span> 如果 <span class="t-spar">表达式</span> 的值是空指针值,则结果是 <span class="t-spar">新类型</span> 类型的空指针值。</div>
|
|
<div class="t-li1"><span class="t-li">3)</span> 如果 <span class="t-spar">新类型</span> 是到 <code>Base</code> 的指针或引用,且 <span class="t-spar">表达式</span> 的类型是到 <code>Derived</code> 的指针或引用,其中 <code>Base</code> 是 <code>Derived</code> 的一个唯一的和可访问的基类,则结果是到 <span class="t-spar">表达式</span> 所标识的对象中 <code>Base</code> 类子对象的指针或引用。(注意:隐式转换和 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">static_cast</span></span></span> 也能进行此转换。)</div>
|
|
<div class="t-li1"><span class="t-li">4)</span> 如果 <span class="t-spar">表达式</span> 是指向<a href="cpp-language-object.html#.E5.A4.9A.E6.80.81.E5.AF.B9.E8.B1.A1">多态类型</a>的指针,且 <span class="t-spar">新类型</span> 是到 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw4">void</span></span></span> 的指针,则结果是指向 <span class="t-spar">表达式</span> 所指向或引用的最终派生对象的指针。</div>
|
|
<div class="t-li1"><span class="t-li">5)</span> 如果 <span class="t-spar">表达式</span> 是到<a href="cpp-language-object.html#.E5.A4.9A.E6.80.81.E5.AF.B9.E8.B1.A1">多态类型</a> <code>Base</code> 的指针或引用,且 <span class="t-spar">新类型</span> 是到 <code>Derived</code> 类型的指针或引用,则进行运行时检查:</div>
|
|
<div class="t-li2"><span class="t-li">a)</span> 检验 <span class="t-spar">表达式</span> 所指向/标识的最终派生对象。若在该对象中 <span class="t-spar">表达式</span> 指向/指代 <code>Derived</code> 的公开基类,且只有一个 <code>Derived</code> 类型对象从 <span class="t-spar">表达式</span> 所指向/标识的子对象派生,则转型结果指向/指代该 <code>Derived</code> 对象。(“向下转型(downcast)”)</div>
|
|
<div class="t-li2"><span class="t-li">b)</span> 否则,若 <span class="t-spar">表达式</span> 指向/指代最终派生对象的公开基类,而同时最终派生对象拥有 <code>Derived</code> 类型的无歧义公开基类,则转型结果指向/指代该 <code>Derived</code>。(“侧向转型(sidecast)”)</div>
|
|
<div class="t-li2"><span class="t-li">c)</span> 否则,运行时检查失败。若 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">dynamic_cast</span></span></span> 用于指针,则返回 <span class="t-spar">新类型</span> 类型的空指针值。若它用于引用,则抛出 <span class="t-lc"><a href="cpp-types-bad_cast.html">std::bad_cast</a></span> 异常。</div>
|
|
<div class="t-li1"><span class="t-li">6)</span> 当在构造函数或析构函数中(直接或间接)使用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">dynamic_cast</span></span></span>,且 <span class="t-spar">表达式</span> 指代正在构造/销毁的对象时,该对象被认为是最终派生对象。若 <span class="t-spar">新类型</span> 不是到构造函数/析构函数自身的类或其基类之一的指针或引用,则行为未定义。</div>
|
|
<p>与其他转型表达式相似:</p>
|
|
<ul>
|
|
<li>若 <span class="t-spar">新类型</span> 是左值引用类型(<span class="t-spar">表达式</span> 必为左值),则其结果为左值</li>
|
|
<li>若 <span class="t-spar">新类型</span> 是右值引用类型(<span class="t-spar">表达式</span> 为完整类类型,<span class="t-rev-inl t-until-cxx17"><span>可以是左值或右值</span> <span><span class="t-mark-rev t-until-cxx17">(C++17 前)</span></span></span><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>),则其结果为亡值</li>
|
|
<li>若 <span class="t-spar">新类型</span> 是指针类型,则其结果为纯右值</li>
|
|
</ul>
|
|
<h3><span class="mw-headline" id=".E6.B3.A8.E8.A7.A3">注解</span></h3>
|
|
<ul>
|
|
<li><span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">static_cast</span></span></span> 也能用来进行向下转型,它避免运行时检查的开销,但只有在程序(通过某些其他逻辑)能够保证 <span class="t-spar">表达式</span> 所指向的对象肯定是 <code>Derived</code> 时才是安全的。</li>
|
|
<li>某些形式的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw1">dynamic_cast</span></span></span> 依赖于<a href="https://en.wikipedia.org/wiki/Run-time_type_information" class="extiw" title="enwiki:Run-time type information">运行时类型鉴别</a>( RTTI ),即编译的程序中关于每个多态类的信息。编译器通常有选项禁用此信息。</li>
|
|
</ul>
|
|
<h3><span class="mw-headline" id=".E5.85.B3.E9.94.AE.E8.AF.8D">关键词</span></h3>
|
|
<p><a href="cpp-keyword-dynamic_cast.html"><tt>dynamic_cast</tt></a></p>
|
|
<h3><span class="mw-headline" id=".E7.A4.BA.E4.BE.8B">示例</span></h3>
|
|
<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="kw1">struct</span> V <span class="br0">{</span>
|
|
<span class="kw1">virtual</span> <span class="kw4">void</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span> <span class="co1">// 必须为多态以使用运行时检查的 dynamic_cast</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> A <span class="sy4">:</span> <span class="kw1">virtual</span> V <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> B <span class="sy4">:</span> <span class="kw1">virtual</span> V <span class="br0">{</span>
|
|
B<span class="br0">(</span>V<span class="sy2">*</span> v, A<span class="sy2">*</span> a<span class="br0">)</span> <span class="br0">{</span>
|
|
<span class="co1">// 构造中转型(见后述 D 的构造函数中的调用)</span>
|
|
<span class="kw1">dynamic_cast</span><span class="sy1"><</span>B<span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span>v<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 良好定义:v 有类型 V*,B 的 V 基类,产生 B*</span>
|
|
<span class="kw1">dynamic_cast</span><span class="sy1"><</span>B<span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 未定义行为:a 有类型 A*,A 非 B 的基类</span>
|
|
<span class="br0">}</span>
|
|
<span class="br0">}</span><span class="sy4">;</span>
|
|
<span class="kw1">struct</span> D <span class="sy4">:</span> A, B <span class="br0">{</span>
|
|
D<span class="br0">(</span><span class="br0">)</span> <span class="sy4">:</span> B<span class="br0">(</span><span class="br0">(</span>A<span class="sy2">*</span><span class="br0">)</span>this, this<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> Base <span class="br0">{</span>
|
|
<span class="kw1">virtual</span> ~Base<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> Derived<span class="sy4">:</span> Base <span class="br0">{</span>
|
|
<span class="kw1">virtual</span> <span class="kw4">void</span> name<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="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span>
|
|
<span class="br0">{</span>
|
|
D d<span class="sy4">;</span> <span class="co1">// 最终派生对象</span>
|
|
A<span class="sy3">&</span> a <span class="sy1">=</span> d<span class="sy4">;</span> <span class="co1">// 向上转型,可以用 dynamic_cast,但不必须</span>
|
|
D<span class="sy3">&</span> new_d <span class="sy1">=</span> <span class="kw1">dynamic_cast</span><span class="sy1"><</span>D<span class="sy3">&</span><span class="sy1">></span><span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 向下转型</span>
|
|
B<span class="sy3">&</span> new_b <span class="sy1">=</span> <span class="kw1">dynamic_cast</span><span class="sy1"><</span>B<span class="sy3">&</span><span class="sy1">></span><span class="br0">(</span>a<span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 侧向转型</span>
|
|
|
|
|
|
Base<span class="sy2">*</span> b1 <span class="sy1">=</span> new Base<span class="sy4">;</span>
|
|
<span class="kw1">if</span><span class="br0">(</span>Derived<span class="sy2">*</span> d <span class="sy1">=</span> <span class="kw1">dynamic_cast</span><span class="sy1"><</span>Derived<span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span>b1<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">"成功从 b1 向下转型到 d<span class="es1">\n</span>"</span><span class="sy4">;</span>
|
|
d<span class="sy2">-</span><span class="sy1">></span>name<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 可以安全调用</span>
|
|
<span class="br0">}</span>
|
|
|
|
Base<span class="sy2">*</span> b2 <span class="sy1">=</span> new Derived<span class="sy4">;</span>
|
|
<span class="kw1">if</span><span class="br0">(</span>Derived<span class="sy2">*</span> d <span class="sy1">=</span> <span class="kw1">dynamic_cast</span><span class="sy1"><</span>Derived<span class="sy2">*</span><span class="sy1">></span><span class="br0">(</span>b2<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">"成功从 b2 向下转型到 d<span class="es1">\n</span>"</span><span class="sy4">;</span>
|
|
d<span class="sy2">-</span><span class="sy1">></span>name<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="co1">// 可以安全调用</span>
|
|
<span class="br0">}</span>
|
|
|
|
delete b1<span class="sy4">;</span>
|
|
delete b2<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">成功从 b2 向下转型到 d</pre></div>
|
|
</div>
|
|
</div>
|
|
<h3><span class="mw-headline" id=".E5.8F.82.E9.98.85">参阅</span></h3>
|
|
<ul>
|
|
<li><a href="cpp-language-const_cast.html"><code>const_cast</code></a></li>
|
|
<li><a href="cpp-language-static_cast.html"><code>static_cast</code></a></li>
|
|
<li><a href="cpp-language-reinterpret_cast.html"><code>reinterpret_cast</code></a></li>
|
|
<li><a href="cpp-language-explicit_cast.html">显式转型</a></li>
|
|
<li><a href="cpp-language-implicit_conversion.html">隐式转换</a></li>
|
|
</ul>
|
|
</div>
|
|
<div class="visualClear"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|