溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

《Effective C++》之條款33:避免遮掩繼承而來(lái)的名稱(chēng)

發(fā)布時(shí)間:2020-04-01 15:16:26 來(lái)源:網(wǎng)絡(luò) 閱讀:761 作者:313119992 欄目:編程語(yǔ)言

《Effective C++》

條款33:避免遮掩繼承而來(lái)的名稱(chēng)

遮掩行為與作用域有關(guān)。例子如下:

int x;//global變量
void someFun()
{
    double x;//local 變量
    std::cin >> x;//讀一個(gè)新值賦予local變量x
}

這個(gè)讀取數(shù)據(jù)的語(yǔ)句指涉的是local變量x,而不是global變量x,因?yàn)閮?nèi)層作用域的名稱(chēng)會(huì)遮掩外圍作用域的名稱(chēng)。

例如:

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf2();
    void mf3();
    ...
};

class Derived : public Base
{
public:
    virtual void mf1();
    void mf4();
    ...
};

此例內(nèi)含一組混含了public和private名稱(chēng),以及一組成員變量和成員函數(shù)名稱(chēng)。這些成員函數(shù)包括pure virtual、impure virtual和non-virtual三種,這是為了強(qiáng)調(diào)我們談的是名稱(chēng),和其他無(wú)關(guān)。

假設(shè)derived class內(nèi)的mf4的實(shí)現(xiàn)碼如下:

void Derived::mf4()
{
    ...
    mf2();
    ...
}

當(dāng)編譯器看到這里使用了mf2,必須估算它指涉什么東西。編譯器的做法是查找各作用域,看看有沒(méi)有某個(gè)名為mf2的申明式。首先查找local作用域(也就是mf4覆蓋的作用域),在那兒沒(méi)有找到任何東西名為mf2.于是查找其他外圍作用域,也就是class Derived覆蓋的作用域。還是沒(méi)找到任何東西名為mf2,于是再往外圍移動(dòng),本例為base class。在那兒編譯器找到一個(gè)名為mf2的東西了,于是停止查找。如果Base內(nèi)還是沒(méi)有mf2,查找動(dòng)作便繼續(xù)下去,首先找內(nèi)含Base的那個(gè)namespace的作用域,最后往global作用域找去。

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);
    ...
};

class Derived : public Base
{
public:
    virtual void mf1();
    void mf3();
    void mf4();
    ...
};

Derived d;
int x;
...
d.mf1();//OK
d.mf1(x);//error!!! Derived::mf1遮掩了Base::mf1
d.md2();//OK
d.mf3();//OK
d.mf3(x);//Error!!! Derived::mf3遮掩了Base::mf3

這些行為背后的基本理由是為了防止你的程序庫(kù)或應(yīng)用框架內(nèi)建立新的derived class 時(shí)附帶從疏遠(yuǎn)的base class繼承重載函數(shù)。不幸的是你通常會(huì)想繼承重載函數(shù)。實(shí)際上如果你正在使用public 繼承而又不繼承那些重載函數(shù),就是違反base和derived class之間的is-a關(guān)系,而is-a是public 繼承的基石。因此你總會(huì)想要推翻C++對(duì)“繼承而來(lái)的名稱(chēng)”的缺省遮掩行為。可以使用using聲明表達(dá)式達(dá)成這一目標(biāo):

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);
    ...
};

class Derived : public Base
{
public:
    using Base::mf1;//讓Base class內(nèi)名為mf1和mf3的所有東西在Derived作用域都可見(jiàn)
    using Base::mf3;
    
    virtual void mf1();
    void mf3();
    void mf4();
    ...
};

Derived d;
int x;
...
d.mf1();//OK
d.mf1(x);//OK,調(diào)用Base::mf1
d.md2();//OK
d.mf3();//OK
d.mf3(x);//OK,調(diào)用Base::mf3

也就是說(shuō)如果你繼承base class并加上重載函數(shù),而你又希望重新定義或覆寫(xiě)其中一部分,那么你必須為那些原本會(huì)被遮掩的每個(gè)名稱(chēng)引入一個(gè)using聲明式,否則某些你希望繼承的名稱(chēng)會(huì)被遮掩。

有時(shí)你并不想繼承base class的所有函數(shù),這是可以理解的。在public繼承下,這絕對(duì)不可能發(fā)生。但是在private形式繼承Base,而Derived唯一想繼承的mf1是那個(gè)無(wú)參數(shù)版本。using聲明式在這里派不上用場(chǎng)。一個(gè)簡(jiǎn)單的轉(zhuǎn)交函數(shù)(forwarding functions)

class Base
{
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    ...
};

class Derived : private Base
{
public:
    virtual void mf1()//轉(zhuǎn)交函數(shù)暗自為inline
    {    Base::mf1(); }
    ...
};

Derived d;
int x;
d.mf1();//OK,調(diào)用的是Derived::mf1
d.mf1(x);//Error!Base::mf1()被遮掩

總結(jié):

  1. derived classes內(nèi)的名稱(chēng)會(huì)遮掩base classes內(nèi)的名稱(chēng)。在public繼承下從來(lái)沒(méi)有人希望如此。

  2. 為了讓被遮掩的名稱(chēng)再見(jiàn)天日,可使用using聲明式或轉(zhuǎn)交函數(shù)(forwarding functions)。

2016-11-09 12:56:26

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI