您好,登錄后才能下訂單哦!
前言:需要了解三者的區別,必須要掌握多態的三個必要條件:
虛函數 純虛函數 虛基類三者區別
1.虛函數是用于多態中virtual修飾父類函數,確保父類指針調用子類對象時,運行子類函數的。 2.純虛函數是用來定義接口的,也就是基類中定義一個純虛函數,基類不用實現,讓子類來實現。 3.虛基類是用來在多繼承中,比如菱形繼承中,如果兩個父類繼承自同一個類,就只實例化一個父類
①虛函數
第一個是沒有使用多態(只用繼承)的一般實現方式:
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;
}
結果:
printf A
printf B
這是早期沒有多態的代碼,缺點:代碼冗余
下面是使用了多態但是沒有引用virtual關鍵字的情況:
int main(int argc, const char * argv[])
{
A *a = new B();
a->printf();
return 0;
}
結果:
printf A
因為類的定義都一樣,所以就沒再寫出來了,當父類指針指向子類對象的時候,如果不使用virtual,父類調用方法的時候還是調用了父類自己的方法,沒有調用子類重寫的方法,所以就沒有實現到多態的作用,我們再來在父類中試試加入virtual關鍵字看看:
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;
}
結果
printf B
virtual是加入到父類中的,子類的代碼盡量加上virtual(方便被繼承的子類實現多態),也可以不加,main函數還是父類指針指向子類對象,結果終于可以打印到子類重寫的方法了,所以證實了虛函數是用于多態中virtual修飾父類該重寫的函數,確保父類指針調用子類對象時運行子類函數的。
② 純虛函數
純虛函數就是抽象接口,使用了純虛函數的類不能被實例化,定義了純虛函數的類不用寫純虛函數的實現,由子類實現,下面看代碼:
class A
{
public:
virtual void printf() =0;
};
void A::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();
return 0;
}
virtual void printf() = 0;這是虛函數的寫法,我在下面寫了虛函數的實現 void A::printf(),其實寫不寫都沒關系,寫了也起不了作用,然后我才main函數中嘗試吧純虛函數的類實例化,結果直接報錯,說明純虛函數是不能實例化的。
int main(int argc, const char * argv[])
{
A *a =newB();//這里使用了多態
a->printf();
return 0;
}
結果:
printf B
把main函數的a指向了子類的對象,結果可以正確打印出子類的方法。由此說明了純虛函數也是為多態服務的,它的作用是定義一個接口,讓子類去實現。
③虛基類
虛基類是c++獨有的東西,因為c++中有多繼承,也是關鍵字virtual相關的定義。
先來說說多繼承,如果爺爺類(暫把父類的父類暫定為爺爺類 ),父類繼承自爺爺類。如果孫類繼承自多個父類(聽起來有點怪異),那么如果不使用虛基類,就會實例化多個爺爺類對象(越說越離奇),編譯器會報錯,說有歧義性。如果父類繼承自虛基類,則可以解決多個父類不會實例化多個爺爺的問題,就是只有一個爺爺。
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;//這里編譯錯誤,flag訪問不明確,因為兩個父類中都有flag變量,歧義
return 0;
}
如果沒有使用虛基類,多個父類繼承自同一個爺爺類,就會產生歧義,為了不產生歧義,代碼可改為(治標不治本):
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;
}
結果:
Grandfather flag = 1
Father1 flag = 2
Father2 flag = 3
3
現在,可以運行了,class Father2:virtual public Grandfather,就是繼承虛基類的寫法,爺爺對象只有一個,爺爺類的變量也只實例化了一次,那為什么最后打印出來的是3呢?看構造函數的順序就可以看出來了,現在構造了爺爺類,再構造第一個繼承的父類,最后繼承第二個繼承的父類,因此flag最后保持在第二個父類的修改值里了。
總的來說,虛函數 ,純虛函數是為了多態服務,虛基類是為了只實例化一次基類存在的
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。