溫馨提示×

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

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

C++中的 虛函數(shù) 純虛函數(shù) 虛基類(virtual)

發(fā)布時(shí)間:2020-07-30 09:05:24 來(lái)源:網(wǎng)絡(luò) 閱讀:435 作者:夢(mèng)T醒 欄目:編程語(yǔ)言

C++中的 虛函數(shù) 純虛函數(shù) 虛基類(virtual)
前言:需要了解三者的區(qū)別,必須要掌握多態(tài)的三個(gè)必要條件:

  1. 繼承
  2. 重載
  3. 父類指針指向子類對(duì)象。

虛函數(shù) 純虛函數(shù) 虛基類三者區(qū)別

1.虛函數(shù)是用于多態(tài)中virtual修飾父類函數(shù),確保父類指針調(diào)用子類對(duì)象時(shí),運(yùn)行子類函數(shù)的。
2.純虛函數(shù)是用來(lái)定義接口的,也就是基類中定義一個(gè)純虛函數(shù),基類不用實(shí)現(xiàn),讓子類來(lái)實(shí)現(xiàn)。
3.虛基類是用來(lái)在多繼承中,比如菱形繼承中,如果兩個(gè)父類繼承自同一個(gè)類,就只實(shí)例化一個(gè)父類

①虛函數(shù)
第一個(gè)是沒(méi)有使用多態(tài)(只用繼承)的一般實(shí)現(xiàn)方式:

class A  
{  
public:  
    void printf(){  
        cout<<"printf A"<<endl;  
    }  
};  
class B : public A  
{  
public:  
    void printf(){  
        cout<<"printf B"<<endl;  
    }  
};  
int main(int argc, const char * argv[])  
{  
    A *a = new A();  
    a->printf();  
    B *b = new B();  
    b->printf();  
    return 0;  
}  

結(jié)果:

printf A
printf B

這是早期沒(méi)有多態(tài)的代碼,缺點(diǎn):代碼冗余
下面是使用了多態(tài)但是沒(méi)有引用virtual關(guān)鍵字的情況:

int main(int argc, const char * argv[])  
{  
    A *a = new B();  
    a->printf();  
    return 0;  
}  

結(jié)果:

printf A

因?yàn)轭惖亩x都一樣,所以就沒(méi)再寫出來(lái)了,當(dāng)父類指針指向子類對(duì)象的時(shí)候,如果不使用virtual,父類調(diào)用方法的時(shí)候還是調(diào)用了父類自己的方法,沒(méi)有調(diào)用子類重寫的方法,所以就沒(méi)有實(shí)現(xiàn)到多態(tài)的作用,我們?cè)賮?lái)在父類中試試加入virtual關(guān)鍵字看看:

class A  
{  
public:  
virtual void printf(){  
        cout<<"printf A"<<endl;  
    }  
};  
class B : public A  
{  
public:  
//子類也可以不使用virtual,直接寫為void printf()
    virtual void printf(){  
        cout<<"printf B"<<endl;  
    }  
};  
int main(int argc, const char * argv[])  
{  
    A *a = new B();  
    a->printf();  
    return 0;  
}  

結(jié)果

printf B

virtual是加入到父類中的,子類的代碼盡量加上virtual(方便被繼承的子類實(shí)現(xiàn)多態(tài)),也可以不加,main函數(shù)還是父類指針指向子類對(duì)象,結(jié)果終于可以打印到子類重寫的方法了,所以證實(shí)了虛函數(shù)是用于多態(tài)中virtual修飾父類該重寫的函數(shù),確保父類指針調(diào)用子類對(duì)象時(shí)運(yùn)行子類函數(shù)的。
② 純虛函數(shù)
純虛函數(shù)就是抽象接口,使用了純虛函數(shù)的類不能被實(shí)例化,定義了純虛函數(shù)的類不用寫純虛函數(shù)的實(shí)現(xiàn),由子類實(shí)現(xiàn),下面看代碼:

class A  
{  
public:  
    virtual void printf() =0;  
};  
void A::printf()//純虛函數(shù)可以不寫實(shí)現(xiàn)  
{  
    cout<<"printf A"<<endl;  
}  
class B : public A  
{  
public:  
    void printf(){  
        cout<<"printf B"<<endl;  
    }  
};  
int main(int argc, const char * argv[])  
{  
    A *a =new A();//編譯出錯(cuò),純虛函數(shù)的類不能實(shí)例化  
    a->printf();  
    return 0;  
}  

virtual void printf() = 0;這是虛函數(shù)的寫法,我在下面寫了虛函數(shù)的實(shí)現(xiàn) void A::printf(),其實(shí)寫不寫都沒(méi)關(guān)系,寫了也起不了作用,然后我才main函數(shù)中嘗試吧純虛函數(shù)的類實(shí)例化,結(jié)果直接報(bào)錯(cuò),說(shuō)明純虛函數(shù)是不能實(shí)例化的。

int main(int argc, const char * argv[])  
{  
    A *a =newB();//這里使用了多態(tài)  
    a->printf();  
    return 0;  
}  

結(jié)果:

printf B

把main函數(shù)的a指向了子類的對(duì)象,結(jié)果可以正確打印出子類的方法。由此說(shuō)明了純虛函數(shù)也是為多態(tài)服務(wù)的,它的作用是定義一個(gè)接口,讓子類去實(shí)現(xiàn)。
③虛基類
虛基類是c++獨(dú)有的東西,因?yàn)閏++中有多繼承,也是關(guān)鍵字virtual相關(guān)的定義。

先來(lái)說(shuō)說(shuō)多繼承,如果爺爺類(暫把父類的父類暫定為爺爺類 ),父類繼承自爺爺類。如果孫類繼承自多個(gè)父類(聽(tīng)起來(lái)有點(diǎn)怪異),那么如果不使用虛基類,就會(huì)實(shí)例化多個(gè)爺爺類對(duì)象(越說(shuō)越離奇),編譯器會(huì)報(bào)錯(cuò),說(shuō)有歧義性。如果父類繼承自虛基類,則可以解決多個(gè)父類不會(huì)實(shí)例化多個(gè)爺爺?shù)膯?wèn)題,就是只有一個(gè)爺爺。

class Grandfather{  
public:  
    int flag;  
    Grandfather(){  
        flag = 1;  
    }  
};  
class Father1:public Grandfather{  
public:  
    Father1(){  
        flag = 2;  
    }  
};  
class Father2:public Grandfather{  
public:  
    Father2(){  
        flag = 3;  
    }  
};  
class Son:public Father1,public Father2{  
};  
int main(int argc, const char * argv[])  
{  
    Son *son = new Son();  
    cout<<son->flag<<endl;//這里編譯錯(cuò)誤,flag訪問(wèn)不明確,因?yàn)閮蓚€(gè)父類中都有flag變量,歧義  
    return 0;  
}  

如果沒(méi)有使用虛基類,多個(gè)父類繼承自同一個(gè)爺爺類,就會(huì)產(chǎn)生歧義,為了不產(chǎn)生歧義,代碼可改為(治標(biāo)不治本):

cout<<son->Father1::flag<<endl;
cout<<son->Father2::flag<<endl;

如果父類繼承虛基類就不同了:

class Grandfather{  
public:  
    int flag;  
    Grandfather(){  
        flag = 1;  
        cout<<"Grandfather flag = "<<flag <<endl;  
    }  
};  
class Father1:virtual public Grandfather{  
public:  
    Father1(){  
        flag = 2;  
        cout<<"Father1 flag = "<<flag<<endl;  
    }  
};  
class Father2:virtual public Grandfather{  
public:  
    Father2(){  
        flag = 3;  
        cout<<"Father2 flag = "<<flag<<endl;  
    }  
};  
class Son:public Father1,public Father2{  
};  
int main(int argc, const char * argv[])  
{  
    Son *son = new Son();  
    cout<<son->flag<<endl;  
    return 0;  
}  

結(jié)果:

Grandfather flag = 1

Father1 flag = 2

Father2 flag = 3

3

現(xiàn)在,可以運(yùn)行了,class Father2:virtual public Grandfather,就是繼承虛基類的寫法,爺爺對(duì)象只有一個(gè),爺爺類的變量也只實(shí)例化了一次,那為什么最后打印出來(lái)的是3呢?看構(gòu)造函數(shù)的順序就可以看出來(lái)了,現(xiàn)在構(gòu)造了爺爺類,再構(gòu)造第一個(gè)繼承的父類,最后繼承第二個(gè)繼承的父類,因此flag最后保持在第二個(gè)父類的修改值里了。

C++中的 虛函數(shù) 純虛函數(shù) 虛基類(virtual)總的來(lái)說(shuō),虛函數(shù) ,純虛函數(shù)是為了多態(tài)服務(wù),虛基類是為了只實(shí)例化一次基類存在的

向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