Item 33: Avoid hiding inherited names.
其實本文的話題和繼承完全沒有關係,隱藏名稱是作用域的問題。 在C++中每一對{ }都會開啓一個新的作用域,並嵌套在當前作用域中。
一個示例
int x;
void func(){
double x;
cin>>x; // read a new value for local x
}
可以看到double x隱藏了int x,因爲C++的名稱隱藏規則(name-hiding rules)隱藏的是名稱,和類型無關!
繼承作用域
子類可以訪問父類中的名稱,是因爲子類的作用域是嵌套(nested in)在父類的作用域中的。 這一點也很符合直覺:
class Base{
public:
void func_base();
};
class Derived{
public:
void func_derived(){
func_base();
}
};
在func_derived()中調用func_base()時,編譯器首先檢查當前作用域內是否有名稱func_base(當然C++是不允許在函數裏定義函數的), 沒有找到;然後去父作用域Derived中尋找名稱func_base,仍然未找到;然後去再上一級作用域Base中尋找func_base,找到了!然後調用Base::func_base()。
如果還沒找到,編譯器還會去Derived所在命名空間下、全局作用域下尋找。
隱藏父類的名稱
子類中重寫(override)與父類方法同名的方法,將會隱藏父類中所有同名的重載方法。例如:
class Base{
public:
virtual void func()=0;
void func(int);
};
class Derived: public Base{
public:
virtual void func();
};
...
Derived d;
d.func(1); // Error!
Derived中聲明的func方法,隱藏了父類中所有的func名稱,包括所有的重載函數。
繼承所有重載方法
當你從父類繼承來了一系列的重載(overload)方法,而只想重寫(override)其中的一個時,可以用using,否則其他重載方法會被隱藏。
class Derived: public Base{
public:
using Base::func;
virtual void func();
};
...
d.func(1); // OK
繼承一個重載方法
在public繼承中,子類和父類是”is-a”的關係(見Item 32),所以通常我們希望從父類繼承所有的方法。 但如果是private繼承(見Item 39), 可能你只想要其中的一個,這時可以定義一個轉發函數(forwarding function):
class Base{
public:
virtual void mf1() = 0;
virtual void mf1(int);
};
class Derived: private Base{
public:
virtual void f1(){
Base::mf1(); // 這是一個inline函數,見 Item30
}
};
總結
- 子類中的名稱會隱藏父類中所有同名的屬性。public繼承表示這”is-a”的關係,應該避免這樣做。
- 使用using聲明或者轉發函數可以使父類名稱再次可見。
轉載地址:http://harttle.land/2015/09/06/effective-cpp-39.html
感謝作者 Harttle