1、對象的靜態類型和動態類型
- 對象的靜態類型:對象在聲明時採用的類型。是在編譯期確定的。
- 對象的動態類型:目前所指對象的類型,是在運行期決定的。對象的動態類型可以更改,但是靜態類型無法更改。
關於對象的靜態類型和動態類型,看一個示例:
classB
{
};
classC:publicB
{
};
classD:publicB
{
};
D*pD=newD(); // pD的靜態類型是它聲明的類型D*,動態類型也是D*
B*pB=pD; // pB的靜態類型是它聲明的類型B*,動態類型是pB所指向的對象pD的類型D*
C*pC=newC();
pB=pC; // pB的動態類型是可以更改的,現在它的動態類型是C*
2、靜態綁定和動態綁定
- 靜態綁定:綁定的是對象的靜態類型,某特性(比如函數)依賴於對象的靜態類型,發生在編譯期。
- 動態綁定:綁定的是對象的動態類型,某特性(比如函數)依賴於對象的動態類型,發生在運行期。
classB
{
voidDoSomething();
virtualvoidvfun();
};
classC:publicB
{
voidDoSomething(); // 首先說明一下,這個子類重新定義了父類的no-virtual函數,這是一個不好的設計,會導致名稱遮掩;這裏只是爲了說明動態綁定和靜態綁定才這樣使用。
virtualvoidvfun();
};
classD:publicB
{
voidDoSomething();
virtualvoidvfun();
};
D*pD=newD();
B*pB=pD;
- pD->DoSomething()和pB->DoSomething()調用的不是同一個函數,雖然pD和pB都指向同一個對象。因爲函數DoSomething是一個no-virtual函數,它是靜態綁定的,也就是編譯器會在編譯期根據對象的靜態類型來選擇函數。pD的靜態類型是D*,那麼編譯器在處理pD->DoSomething()的時候會將它指向D::DoSomething()。同理,pB的靜態類型是B*,那pB->DoSomething()調用的就是B::DoSomething()。
- pD->vfun()和pB->vfun()調用的是同一個函數,因爲vfun是一個虛函數,它動態綁定的,也就是說它綁定的是對象的動態類型,pB和pD雖然靜態類型不同,但是他們同時指向一個對象,他們的動態類型是相同的,都是D*,所以,他們的調用的是同一個函數:D::vfun()。
- 上面都是針對對象指針的情況,對於引用(reference)的情況同樣適用。指針和引用的動態類型和靜態類型可能會不一致,但是對象的動態類型和靜態類型是一致的。
D D;
D.DoSomething()和D.vfun()永遠調用的都是D::DoSomething()和D::vfun()。
- 至於哪些是動態綁定,哪些是靜態綁定,總結爲一句話:只有虛函數才使用的是動態綁定,其他的全部是靜態綁定。
3、一個困惑
看完上述講解你可能有一個疑惑,即:“我看程序代碼時,可以看出實際將會調用的函數,爲何編譯器不能在編譯時確定將要調用的函數”
一個示例來解決你的困惑:
Object* obj;
std::cin >> type;
switch (type)
{
case PEOPLE: obj = new People;
break;
case MUSLIM: obj = new Sheep;
break;
}
obj->WhichFunc();
此時你還能看出將要調用哪個函數麼?哈哈哈