函數重載:
在一個類中,函數名相同,函數參數類型,或者函數參數個數不同,或者都不同,成爲函數重載。
函數的重載發生在程序編譯階段,由編譯器進行優化和區分。
《高質量C++/C編程指南》已經清晰的列出了重載函數的特性:
(1)相同的範圍(在同一個類中);
(2)函數名字相同;
(3)參數不同;
(4)virtual關鍵字可有可無。
如:
class A
{
public:
A()
{
age = 3;
}
A(int num)
{
age = num;
}
private:
int age;
};
如A()與A(int num)同爲A的構造函數,但由於參數不同,則兩則互爲重載,對於一個實例,編譯器會根據參數的個數執行不同的構造函數
函數的隱藏和覆蓋只發生在子類和父類之間,函數隱藏發生在編譯時期,相對於對象指針爲固定偏移,函數覆蓋發生在運行時期,基類和子類的虛函數會根據虛函數列表到指向出運行虛函數。
對於父類和子類中同名函數,如果沒有virtual關鍵字修飾,編譯器都將子類中的基類同名函數做隱藏處理;如果有virtual關鍵字修飾,編譯器會建立虛函數列表,運行時期,根據虛函數表運行相應的虛函數。
函數隱藏
函數隱藏,是指派生類函數將基類函數給藏起來了,即函數隱藏發生在子類和父類之間。特性爲:
- 如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏。
- 如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數別隱藏。
#include <iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("1\n");
}
};
class B :public A
{
public:
void foo()
{
printf("3\n");
}
};
int main(void)
{
A a;
A *p_A = &a;
p_A->foo();//執行成員函數
B b;
B *p_B = &b;
p_B->foo();//執行成員函數
p_A = &b; //p_A指針執行子類,可以訪問被隱藏的基類成員函數
p_A->foo();
system("pause");
return 0;
}
輸出爲1 3 1
執行成員函數的輸出爲1,3,這裏沒有問題。B中成員函數foo()與基類A中成員函數foo,函數名相同,且沒有virtual關鍵字,則子類會將基類中的同名函數隱藏,但是,通過用基類指針指向子類,可以訪問被隱藏的基類同名函數foo().
同時也說明,除數據區以外,子類會繼承基類所有的成員函數,但對於同名的成員函數,子類在實現自身的成員函數後,會將基類的同名函數隱藏(這裏的隱藏指不能通過子類型的對象指針訪問到基類的隱藏指針,但可以通過基類指針訪問到被隱藏的子類指針中的基類指針)
圖爲子類父類中成員函數在內存中的排布
函數覆蓋
函數的覆蓋是指派生類函數覆蓋基類函數,只作用與派生類函數,其特性爲:
- 不同的範圍(分別位於派生類與基類);
- 函數名字相同;
- 參數相同;
- 基類函數必須有virtual關鍵字。
實際上虛函數的作用,就是實現覆蓋
函數的覆蓋發生在執行時期,對於有虛函數的類,都含有虛函數表,其虛函數表的指針偏移量固定,但表中函數所指向的函數地址不同,運行時期,會根據虛函數表去執行對應的虛函數。
#include<iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("1\n");
}
virtual void fun()
{
printf("2\n");
}
};
class B : public A
{
public:
void foo()
{
printf("3\n");
}
void fun()
{
printf("4\n");
}
};
int main(void)
{
A a;
B b;
A *p = &a;
p->foo(); //訪問基類成員函數
p->fun(); //訪問基類虛函數
p = &b;
p->foo(); //訪問子類成員函數
p->fun(); //訪問基類虛函數
B *ptr = (B *)&a;
ptr->foo(); //訪問子類成員函數
ptr->fun(); //訪問基類成員函數
system("pause");
return 0;
}
輸出爲1 2 1 4 3 2
基類和子類中的同名函數,根據是否有virtual關鍵字修飾分爲成員函數和虛函數,對於成員函數基類做隱藏處理,對於虛函數,子類重新實現,並更新虛函數表,基類和子類都是通過訪問虛函數表間接訪問虛函數。
即,對象指針訪問成員函數時,會根據指針的類型,偏移確定的間隔,子類可以通過跳轉基類的偏移來訪問子類中被隱藏的基類同名函數;對象指針訪問虛函數時,會根據虛函數表的指向去執行虛函數。