四、c++中的多態(tài)規(guī)則。一) c++中函數(shù)動態(tài)綁定規(guī)則。看下面的例子:
類TextWindow繼承與類Window。我想程序運(yùn)行的結(jié)果,大多數(shù)熟悉C++的人都會知道,win.oops()最終調(diào)用的是父類oops函數(shù),而tWin->oops()調(diào)用的是子類TextWindow的函數(shù)。通過這個例子,我們先總結(jié)一下c++中的多態(tài)調(diào)用規(guī)則:第一、對于指針和引用類型,當(dāng)消息調(diào)用的成員函數(shù)有可能被重寫時(shí),最終被選擇調(diào)用的成員函數(shù)由消息接收者的動態(tài)類型確定(注意:在OO概念中,對某個對象成員函數(shù)進(jìn)行調(diào)用,常常稱為給該對象發(fā)送消息,該對象就是消息的接收者)。 如上例:tWin->oops(),由于tWin的動態(tài)類型是子類TextWindow,而不是Windows,所以tWin->oops()調(diào)用的是子類TextWindow的函數(shù)。第二、對于其它的變量,對虛擬函數(shù)調(diào)用綁定完全由該變量的靜態(tài)類型確定(即該變量的聲明),而不是該變量的真實(shí)類型確定。 如上例:win.oops(),由于win的聲明類型為Window類,所以其結(jié)果調(diào)用的是父類oops函數(shù)。二) 探討。 接下來,我們要看的問題是,在c++中,為什么對于多態(tài)規(guī)則(或者說是動態(tài)函數(shù)綁定規(guī)則),做出了兩中不同的劃分,即:只有指針與引用類型,才進(jìn)行函數(shù)的后期動態(tài)綁定,也就是多態(tài)。這或許也是許多c++初學(xué)者非常迷惑的地方。這種規(guī)則的不一致性,的確給c++的語法造成一定的復(fù)雜性。而這在Java,或者C#中是沒有的,后面我們會涉及到。 我們先來看例子。
在這里,如果我們假設(shè),c++的函數(shù)動態(tài)綁定規(guī)則是一致的,看看會發(fā)生什么問題??? 現(xiàn)在win被聲明為Window類型,然而其真實(shí)的類型為TextWindow(因?yàn)閣in=*tWinPtr),由于我們的假設(shè),win現(xiàn)在是允許進(jìn)行動態(tài)函數(shù)綁定的,所以當(dāng)執(zhí)行win.oops()時(shí),實(shí)際上是調(diào)用子類TextWindow的成員函數(shù)。 現(xiàn)在,我們有必要來審視一下win變量的內(nèi)存布局。由于win變量是在棧上聲明的變量,其內(nèi)存也是從棧進(jìn)行分配(這是c++從c語言那里繼承過來的優(yōu)良特質(zhì),從棧上分配內(nèi)存空間比動態(tài)分配內(nèi)存有更好的執(zhí)行速度),c++標(biāo)準(zhǔn)規(guī)定:給win變量分配內(nèi)存空間的大小,由其靜態(tài)的類型確定,即應(yīng)該是Window類所使用的內(nèi)存空間大小。在這種情況下,當(dāng)執(zhí)行win=*tWinPtr時(shí),什么會發(fā)生?如下圖:在默認(rèn)的拷貝構(gòu)造函數(shù)情況下,信息會出現(xiàn)丟失,這就是著名的slicing off現(xiàn)象。結(jié)果,變量cursorLocation在win的內(nèi)存空間里丟失了。然而,問題是:在我們假設(shè)下,我們要求win.oops()導(dǎo)致TextWindow的成員函數(shù)調(diào)用,而在這個函數(shù)中,訪問到的cursorLocation變量是不存在!win.oops()調(diào)用將導(dǎo)致內(nèi)存違例! 到這里,我們可以總結(jié)一下:c++標(biāo)準(zhǔn)基于的其特定的內(nèi)存分配規(guī)則,給出了以上,我們在前一節(jié)總結(jié)出的函數(shù)動態(tài)綁定規(guī)則。三) 深入。 當(dāng)然,我們也可以說,c++也可以通過改變其內(nèi)存分配規(guī)則,來給出一個一致性的函數(shù)動態(tài)綁定規(guī)則。比如:可以考慮在給win變量分配內(nèi)存空間時(shí),考慮其所有子類需求,然后分配最大數(shù)量的內(nèi)存空間給win變量。這種做法可行性很差,對于編譯器而言,需要掃描整個程序(確定該類的所有子類),才能確定最大的內(nèi)存空間是多少。在使用類庫或者框架的情況下,會引起整個類庫,框架的重新編譯,這是得不償失的!而這種做法,在oo的語言中,基本上是沒有的。這也是c++不得不基于其現(xiàn)有的內(nèi)存管理機(jī)制,而對多態(tài)規(guī)則作出的不一致的解釋。 對于這個c++現(xiàn)有的內(nèi)存管理機(jī)制,我們?nèi)绻麖牧硗饨嵌热ダ斫獾脑?是很合理的。當(dāng)win=*tWinPtr發(fā)生時(shí),我們可以類似地認(rèn)為:好比一個float類型的數(shù)賦給了一個interger類型的變量,其結(jié)果當(dāng)然是float的值被截?cái)嗔恕?br> 我們再來看其它語言,Java(或者C#)是怎么解決的。 最重要的一點(diǎn)是,在Java(C#)中只有引用的概念,所以在棧上聲明的類的變量,只需要分配一個指針大小的內(nèi)存空間就行了,而不需要給該變量分配空間來保存變量內(nèi)容本身,這其實(shí)就是我們現(xiàn)在看到的c++中指針和引用的情況。
Powered by: C++博客 Copyright © 愛上龍卷風(fēng)