《Effective C++》
條款33:避免遮掩繼承而來的名稱
遮掩行為與作用域有關。例子如下:
1
2
3
4
5
6
<code>int</code> <code>x;</code><code>//global變量</code>
<code>void</code> <code>someFun()</code>
<code>{</code>
<code> </code><code>double</code> <code>x;</code><code>//local 變量</code>
<code> </code><code>std::cin >> x;</code><code>//讀一個新值賦予local變量x</code>
<code>}</code>
這個讀取資料的語句指涉的是local變量x,而不是global變量x,因為内層作用域的名稱會遮掩外圍作用域的名稱。
例如:
7
8
9
10
11
12
13
14
15
16
17
18
<code>class</code> <code>Base</code>
<code>private</code><code>:</code>
<code> </code><code>int</code> <code>x;</code>
<code>public</code><code>:</code>
<code> </code><code>virtual</code> <code>void</code> <code>mf1() = 0;</code>
<code> </code><code>virtual</code> <code>void</code> <code>mf2();</code>
<code> </code><code>void</code> <code>mf3();</code>
<code> </code><code>...</code>
<code>};</code>
<code>class</code> <code>Derived : </code><code>public</code> <code>Base</code>
<code> </code><code>virtual</code> <code>void</code> <code>mf1();</code>
<code> </code><code>void</code> <code>mf4();</code>
此例内含一組混含了public和private名稱,以及一組成員變量和成員函數名稱。這些成員函數包括pure virtual、impure virtual和non-virtual三種,這是為了強調我們談的是名稱,和其他無關。
假設derived class内的mf4的實作碼如下:
<code>void</code> <code>Derived::mf4()</code>
<code> </code><code>mf2();</code>
當編譯器看到這裡使用了mf2,必須估算它指涉什麼東西。編譯器的做法是查找各作用域,看看有沒有某個名為mf2的申明式。首先查找local作用域(也就是mf4覆寫的作用域),在那兒沒有找到任何東西名為mf2.于是查找其他外圍作用域,也就是class Derived覆寫的作用域。還是沒找到任何東西名為mf2,于是再往外圍移動,本例為base class。在那兒編譯器找到一個名為mf2的東西了,于是停止查找。如果Base内還是沒有mf2,查找動作便繼續下去,首先找内含Base的那個namespace的作用域,最後往global作用域找去。
19
20
21
22
23
24
25
26
27
28
29
30
<code> </code><code>virtual</code> <code>void</code> <code>mf1(</code><code>int</code><code>);</code>
<code> </code><code>void</code> <code>mf3(</code><code>double</code><code>);</code>
<code>Derived d;</code>
<code>int</code> <code>x;</code>
<code>...</code>
<code>d.mf1();</code><code>//OK</code>
<code>d.mf1(x);</code><code>//error!!! Derived::mf1遮掩了Base::mf1</code>
<code>d.md2();</code><code>//OK</code>
<code>d.mf3();</code><code>//OK</code>
<code>d.mf3(x);</code><code>//Error!!! Derived::mf3遮掩了Base::mf3</code>
這些行為背後的基本理由是為了防止你的程式庫或應用架構内建立新的derived class 時附帶從疏遠的base class繼承重載函數。不幸的是你通常會想繼承重載函數。實際上如果你正在使用public 繼承而又不繼承那些重載函數,就是違反base和derived class之間的is-a關系,而is-a是public 繼承的基石。是以你總會想要推翻C++對“繼承而來的名稱”的預設遮掩行為。可以使用using聲明表達式達成這一目标:
31
32
33
<code> </code><code>using</code> <code>Base::mf1;</code><code>//讓Base class内名為mf1和mf3的所有東西在Derived作用域都可見</code>
<code> </code><code>using</code> <code>Base::mf3;</code>
<code> </code>
<code>d.mf1(x);</code><code>//OK,調用Base::mf1</code>
<code>d.mf3(x);</code><code>//OK,調用Base::mf3</code>
也就是說如果你繼承base class并加上重載函數,而你又希望重新定義或覆寫其中一部分,那麼你必須為那些原本會被遮掩的每個名稱引入一個using聲明式,否則某些你希望繼承的名稱會被遮掩。
有時你并不想繼承base class的所有函數,這是可以了解的。在public繼承下,這絕對不可能發生。但是在private形式繼承Base,而Derived唯一想繼承的mf1是那個無參數版本。using聲明式在這裡派不上用場。一個簡單的轉交函數(forwarding functions)
<code>class</code> <code>Derived : </code><code>private</code> <code>Base</code>
<code> </code><code>virtual</code> <code>void</code> <code>mf1()</code><code>//轉交函數暗自為inline</code>
<code> </code><code>{ Base::mf1(); }</code>
<code>d.mf1();</code><code>//OK,調用的是Derived::mf1</code>
<code>d.mf1(x);</code><code>//Error!Base::mf1()被遮掩</code>
總結:
derived classes内的名稱會遮掩base classes内的名稱。在public繼承下從來沒有人希望如此。
為了讓被遮掩的名稱再見天日,可使用using聲明式或轉交函數(forwarding functions)。
2016-11-09 12:56:26
本文轉自313119992 51CTO部落格,原文連結:http://blog.51cto.com/qiaopeng688/1871037