C++中的虛函數
1.題目
先看一下源代碼,如下:
#include <iostream>
using namespace std;
class A
{
public:
void virtual print()
{
cout << "A" << endl;
}
};
class B : public A
{
public:
void virtual print()
{
cout << "B" << endl;
}
};
int main()
{
A* pA = new A();//父類指針
pA->print();//父類指針子類函數
B* pB = (B*)pA;//強制類型轉換
pB->print();//子類指針父類函數
delete pA, pB;//
pA = new B();//父類指針聲明子類對象
pA->print();//父類指針
pB = (B*)pA;//
pB->print();//子類指針
return 0;
}
程序的輸出:
A
A
B
B
2.虛函數
以下出現的所有函數都是虛函數
虛函數表:類中有虛函數,該類實例出的對象會有一個虛函數表
2.0 無繼承
類Base如下:
類 | 函數1 | 函數2 | 函數3 |
---|---|---|---|
Base | +f():void | +g():void | +h():void |
對於實例 Base b; 的虛函數表如下:
地址 | 函數1 | 函數2 | 函數3 |
---|---|---|---|
&b | Base::f() | Base::g() | Base::h() |
2.1 一般繼承
2.1.1 無虛函數覆蓋
類 | 函數1 | 函數2 | 函數3 |
---|---|---|---|
父類Base | +f():void | +g():void | +h():void |
子類Derive | +f1():void | +g1():void | +h1():void |
對於 b=new Base(); 實例的虛函數表:不需要管b是誰的
地址 | 函數1 | 函數2 | 函數3 |
---|---|---|---|
&b | Base::f() | Base::g() | Base::h() |
對於 d=new Derive(); 實例的虛函數表:不需要管d是誰的
地址 | 函數1 | 函數2 | 函數3 | 函數4 | 函數5 | 函數6 |
---|---|---|---|---|---|---|
&d | Base::f() | Base::g() | Base::h() | Derive::f1() | Derive::g1() | Derive::h1() |
2.1.2 有虛函數覆蓋
類 | 函數1 | 函數2 | 函數3 |
---|---|---|---|
父類Base | +f():void | +g():void | +h():void |
子類Derive | +f():void | +g1():void | +h1():void |
對於 d=new Derive(); 實例的虛函數表:不需要管d是誰的
地址 | 函數1 | 函數2 | 函數3 | 函數4 | 函數5 |
---|---|---|---|---|---|
&d | Derive::f() | Base::g() | Base::h() | Derive::g1() | Derive::h1() |
如上所示:子類中的虛函數會覆蓋父類的虛函數,並且在虛函數表中將原父類的位置覆蓋掉。
2.2 多重繼承
2.2.1 無虛函數覆蓋
類 | 函數1 | 函數2 | 函數3 |
---|---|---|---|
父類1Base1 | +f():void | +g():void | +h():void |
父類2Base2 | +f():void | +g():void | +h():void |
父類3Base3 | +f():void | +g():void | +h():void |
子類Derive | +f1():void | +g1():void |
對於 d=new Derive(); 實例的虛函數表:不需要管d是誰的
地址 | 函數1 | 函數2 | 函數3 | 函數4 | 函數5 |
---|---|---|---|---|---|
&d | Base1::f() | Base1::g() | Base1::h() | Derive::f1() | Derive::g1() |
- | Base2::f() | Base2::g() | Base2::h() | ||
- | Base3::f() | Base3::g() | Base3::h() |
子類實例中的虛函數會按照原來的順序,僅僅放置在第一個繼承的鏈表的最後方,保證尋址時的唯一性。
2.2.2 有虛函數覆蓋
類 | 函數1 | 函數2 | 函數3 |
---|---|---|---|
父類1Base1 | +f():void | +g():void | +h():void |
父類2Base2 | +f():void | +g():void | +h():void |
父類3Base3 | +f():void | +g():void | +h():void |
子類Derive | +f():void | +g1():void |
對於 d=new Derive(); 實例的虛函數表:不需要管d是誰的
地址 | 函數1 | 函數2 | 函數3 | 函數4 |
---|---|---|---|---|
&d | Derive::f() | Base1::g() | Base1::h() | Derive::g1() |
- | Derive::f() | Base2::g() | Base2::h() | |
- | Derive::f() | Base3::g() | Base3::h() |
會把每一個父類中覆蓋的虛函數都替換掉。
3.參考鏈接
4.又一道虛函數題目
代碼如下:
#include <iostream>
using namespace std;
struct Base{
int i;
virtual int f(){
cout<<"a";
return 1;
}
virtual const Base &f() const{
cout<<"b";
return *this;//不知道翻墜這句是什麼作用
}
int g(){
cout<<"c";
return 3;
}
};
struct Derive:Base{
int i;
int f(){
cout<<"d";
return 4;
}
const Base &f() const{
cout<<"e";
return *this;
}
int f(int =0){
cout<<"f";
return 6;
}
virtual int g(){
cout<<"g";
return 7;
}
};
int main(){
Derive d;
const Derive d_const;
Base b,*p=&d;
const Base *p_const=&d_const;
b.f();
p->f();
p->g();
p_const->f();
d_const.f();
}
所以說上面的代碼應該輸出什麼呢?
adcee
解釋:
- b.f(); 基類對象直接調用基類的f()函數,輸出a;
- p->f(); 派生類對象賦給基類的指針,由於f()在基類中是虛函數,根據基類指針指向的對象進行調用,因此調用派生類的int f()輸出d;
- p->g();基類中g()不是虛函數,調用基類的g();
- p_const->f();常對象,又由於基類中聲明爲虛,同理用派生類中的函數;
- 同理。
更爲經典的解釋:
- 只有通過基類指針(或引用)間接指向派生類子類型時多態性纔會起作用。
- 派生類的指針只調用自己的函數!
- 基類指針的函數調用,如果有virtual則根據多態性調用派生類的函數;
- 如果沒有virtual則是正常調用基類的函數。