天天看點

派生類向基類轉換的可通路性

最近寫程式時遇到了一個問題,涉及到了派生類向基類轉換的可通路性,也就是帶有通路控制标号的繼承,以前沒有注意到這塊知識,給自己挖了個坑,現在寫出來,當是一次總結,也希望能幫到遇到類似問題的人。

先說我遇到的問題吧,直接用代碼說:

class ProblemBase
{
};

class ProblemD : private ProblemBase
{
};

class ProblemDD : private ProblemD
{
public:
    ProblemBase& getProblemBase()
    {
        return static_cast<ProblemBase&>(*this);
    }
};

int main()
{
}
           

上面的代碼無法通過編譯,提示‘class ProblemBase ProblemBase :ProblemBase‘ is inaccessible。把calss ProbelmD : private  ProblemBase中的private改為public後,能夠通過編譯。或者把static_cast那行注掉也能通過編譯。這就說明問題就出在這個轉換上。于是查詢了一下工具書,發現書中有講到(知識盲點)。

C++ primaer(第五版)》裡有這樣一段話:

派生類向基類的轉換是否可通路由使用該轉換的代碼決定,同時派生類的派生通路說明符也會有影響。假定D繼承自B:

1.隻有當D公有地繼承B時,使用者代碼才能使用派生類向基類的轉換;如果D繼承B的方式是受保護的或者私有的,則使用者代碼

不能使用該轉換。

2.不論D以什麼方式繼承B,D的成員函數和友元都能使用派生類向基類的轉換;派生類向其直接基類的類型轉換對于派生類的

成員和友元來說永遠是可通路的。

3.如果D繼承B的方式是受保護的或者公有的,則D的派生類的成員和友元可以使用D向B的類型轉換;反之,如果D繼承B的方式是私有的,

則不能使用。

總之,對于代碼中的某個給定節點來說,如果基類的公有成員是可通路的,則派生類向基類的類型轉換也是可通路的;反之則不行。

感覺上述代碼其實并沒有直接印證在以上三點,下面我們先通過測試代碼來證明以上三點,在代碼測試中講述我的出錯代碼時如何印證在上面的。

class B
{
};


class DPubB : public B
{
public:
    B& getB()
    {
        return static_cast<B&>(*this); //公有繼承,類代碼可以使用轉換
    }
};


class DProB : protected B
{
public:    
    B& getB()
    {
        return static_cast<B&>(*this); //雖然是受保護繼承,但是類代碼仍使用轉換,印證2
    }
};


class DPriB : private B
{
public:
    B& getB()
    {
        return static_cast<B&>(*this); //雖然是私有繼承,但是類代碼仍使用轉換,印證2
    }
};


class DD1 : public DPriB
{
    //B& getBFrom_DPriB()
    //{
    //    return static_cast<B&>(static_cast<DPriB&>(*this)); //DD1先轉換為DPriB,再由DPriB轉換為B時由于3不能完成轉換
    //}


    //B& getBFromDD1()
    //{
    //    return static_cast<B&>(*this); //DD1直接轉換為B,為了便于可以分解為上面兩步,也不能完成轉換
    //}    
};


class DD2 : private DProB
{
public:
    B& getBFrom_DProB()
    {
        return static_cast<B&>(static_cast<DProB&>(*this)); //DD2先轉換位DPriB,再由DPriB轉換為B時由于3能夠完成轉換
    }


    B& getB1From_DD2()
    {
        return static_cast<B&>(*this); //DD2直接轉換為B,為了便于可以分解為上面兩步,也能夠完成轉換
    } 
};


int main()
{
    B* b1 = new DPubB();
    //B* b2 = new DProB();
    //B* b3 = new DPriB(); //使用者代碼不能使用私有、受保護繼承的轉換,印證1
}
           

經過上述代碼,我們可以發現,其實我遇到的問題就是DD1。

當出現B:A, C:B時根據繼承标号有9種情況,而我們隻讨論了2種,其實足夠了。因為public繼承都不可以轉換,private,protected繼承更不可以;private能夠轉換,protected,public更能轉換。讨論了兩種,其實已經涵蓋了9種。

好了,派生類向基類轉換的可通路性,就讨論到這裡,希望能對你有所幫助。