天天看點

二義性C++中常見的兩種二義性問題及其解決方式

自然語言的二義性什麼意思

面這個問題.很清楚的說明了自然語言的二義性..

用紅墨水寫一個“藍”字,請問,這個字是紅字還是藍字?

可能很少有人意識到,像紅字和藍字這樣的詞語都存在着二義性。可能是紅色的字,也可能是“紅”這個字。

二義性定義:如果文法G中的某個句子存在不隻一棵文法樹,則稱該句子是二義性的。如果文法含有二義性的句子,則稱該文法是二義性的。

C++中常見的兩種二義性問題及其解決方式

--------------------------------一、“倒三角”二義性問題-------------------------------

問題描述:鹵煮之是以稱之為“倒三角問題”,是因為這一類二義性問題所處的繼承體系類似于倒三角形狀,如圖:

二義性C++中常見的兩種二義性問題及其解決方式

這樣,在子類中就存在父類A、B的兩份show(),在調用的時候就會出現二義性問題,這種問題該怎麼解決呢?

面對問題:

//這裡的“倒三角”問題二義性怎麼解決?即有兩個基類共同派生出一個子類,這兩個基類中又同時存在相同的功能的時候

//派生出的子類在調用該功能的時候也會出現二義性問題  這時候該怎麼解決?

//解決方法:利用區域限定符(::)  詳細解決方案見實驗tempt

*/

/*
class grandpa
{
public:
    void show()
    {
        cout<<"This is grandpa\n";
    }
};
class father
{
public:
    void show()
    {
        cout<<"This is father\n";
    }
};
class son:virtual public grandpa,virtual public father
{
public:
    void display()
    {
        cout<<"This is son\n";
    }
};
int main()
{
    son son;
    son.show();
    son.display();
    cout << "Hello world!" << endl;
    return 0;
}
           

//這裡的“倒三角”問題二義性怎麼解決?即有兩個基類共同派生出一個子類,這兩個基類中又同時存在相同的功能的時候

//派生出的子類在調用該功能的時候也會出現二義性問題  這時候該怎麼解決?

//解決方法:利用區域限定符(::)  詳細解決方案見實驗tempt

*/

解決方法:區域限定符(::)

#include <iostream>

using namespace std;
//下面這種情況出現的二義性怎麼解決?
class grandpa
{
public:
    void show()
    {
        cout<<"This is grandpa\n";
    }
};
class father
{
public:
    void show()
    {
        cout<<"This is father\n";
    }
};
class son:virtual public grandpa,virtual public father
{
public:
    void display()
    {
        cout<<"This is son\n";
    }
};
int main()
{
    son son;
    son.father::show();//“倒三角”問題出現的二義性利用區域限定符(::)來解決
    son.grandpa::show();
    son.display();
    return 0;

}
           

-------------------------------------------二、“恐怖菱形”二義性問題---------------------------------------

面對問題:

描述:有最基類A,有A的派生類B、C,又有D同時繼承B、C,那麼若A中有對象a,那麼在派生類B,C中就存在a,又D繼承了B,C,那麼D中便同時存在B繼承A的a和C繼承A的a,那麼當D的執行個體調用a的時候就不知道該調用B的a還是C的a,就導緻了二義性。

 圖示:

二義性C++中常見的兩種二義性問題及其解決方式

解決方案:  虛基類、虛繼承

教科書上面對C++虛基類的描述玄而又玄,名曰“共享繼承”,名曰“各派生類的對象共享基類的的一個拷貝”,其實說白了就是解決多重多級繼承造成的二義性問題。例如有基類B,從B派生出C和D,然後類F又同時繼承了C和D,現在類F的一個對象裡面包含了兩個基類B的對象,如果F通路自己的從基類B那裡繼承過來的的資料成員或者函數成員那麼編譯器就不知道你指的到底是從C那裡繼承過來的B對象呢還是從D那裡繼承過來的B對象。

于是虛基類誕生了,将C和D的繼承方式改為虛繼承,那麼F通路自己從B那裡繼承過來的成員就不會有二義性問題了,也就是将F對象裡的B對象統一為一個,隻有一個基類B對象,下面是一段代碼說明了對虛基類的使用。

#iclude <iostream>

using namespace std;

class A
{
    public:
    int i;
    void showa(){cout<<"i="<<i<<endl;}
};

class B:virtual public A      //此處采用虛繼承
{
    public:
    int j;
};

class C:virtual public A      //此處采用虛繼承
{
    public:
    int k;
};

class D:public B,public C
{
    public:
    int m;
};
int main()
{
    A a;
    B b;
    C c;
    a.i=1;
    a.showa();
    b.i=2;
    b.showa();
    c.i=3;
    c.showa();
    D d;
    d.i=4;
    d.showa();
    //cout << "Hello world!" << endl;
    return 0;
}
           

從這個代碼我們可以看出B,C,D從A那裡繼承過來了i這個變量并且它們之間不會有任何影響,如果B和C不是虛繼承方式的,那麼d.i=4;就不能編譯通過了。