国产18禁黄网站免费观看,99爱在线精品免费观看,粉嫩metart人体欣赏,99久久99精品久久久久久,6080亚洲人久久精品

計(jì)算機(jī)二級(jí)C++輔導(dǎo):面向?qū)ο笳Z(yǔ)言概論(二)

時(shí)間:2009-04-10 15:20:00   來(lái)源:無(wú)憂考網(wǎng)     [字體: ]
Subsumption和Dynamic Dispatch
  從上述的幾個(gè)例子來(lái)看,似乎子類只是用來(lái)從父類借用一些定義,以避免重復(fù)。但是,當(dāng)我們考慮到subsumption, 事情就有些不同了。什么是Subsumption呢?請(qǐng)看下面這個(gè)例子:
  var myCell: InstanceTypeOf(cell) := new cell;
  var myReCell: InstanceTypeOf(reCell) := new reCell;
  procedure f(x: InstanceTypeOf(cell)) is … end;
  再看下面這段代碼:
  myCell := myReCell;
  f(myReCell);
  在這兩行代碼中,頭一行把一個(gè)InstanceTypeOf(reCell)類型的變量賦值給一個(gè)InstanceTypeOf(cell)的變量。而第二行則用InstanceTypeOf(reCell)類型的變量作為參數(shù)傳遞給一個(gè)參數(shù)類型為InstanceTypeOf(cell)的函數(shù)。
  這種用法在類似Pascal的語(yǔ)言中是不合法的。而在面向?qū)ο蟮恼Z(yǔ)言中,依據(jù)以下的規(guī)則,它則是完全正確的用法。該規(guī)則通常被叫做subtype polimorphism, 即子類型多態(tài)(譯者按:其實(shí)subtyping應(yīng)該是OO語(yǔ)言最區(qū)別于其它語(yǔ)言的地方了)
  如果c’是c的子類,并且o’是c’的一個(gè)實(shí)例,那么o’也是c的一個(gè)實(shí)例。
  更嚴(yán)格地說(shuō):
  如果c’是c的子類,并且o’: InstanceTypeOf(c’),那么o’: InstanceTypeOf( c ).
  仔細(xì)分析上面這條規(guī)則,我們可以在InstanceTypeOf的類型之間引入一個(gè)滿足自反和傳遞性的子類型關(guān)系, 我們用<:符號(hào)來(lái)表示。(譯者按:自反就是說(shuō), 對(duì)任何a, a 關(guān)系 a都成立,比如說(shuō),數(shù)學(xué)里的相等關(guān)系就是自反的。而傳遞性是說(shuō),如果a 關(guān)系 b, b 關(guān)系c, 就能推出a 關(guān)系c。 大于,小于等關(guān)系都是具備傳遞性的)
  那么上面這條規(guī)則可以被拆成兩條規(guī)則:
  1. 對(duì)任何a: A, 如果 A <: B, 那么 a: B.
  2. InstanceTypeOf(c’) <: InstanceTypeOf(c) 當(dāng)且僅當(dāng) c’是c的子類
  第一條規(guī)則被叫做Subsumption. 它是判斷子類型(注意,是subtype, 不是subclass)的標(biāo)準(zhǔn)。
  第二條規(guī)則可以叫做subclassing-is-subtyping (子類就是子類型,繞嘴吧?)
  一般來(lái)說(shuō),繼承都是和subclassing相關(guān)的,所以這條規(guī)則也可以叫做:inheritance-is-subtyping (繼承就是子類型)
  所有的面向?qū)ο笳Z(yǔ)言都支持subsumption (可以說(shuō),沒有subsumption, 就不成為面向?qū)ο?。
  大部分的基于類的面向?qū)ο笳Z(yǔ)言也并不區(qū)分subclassing和subtyping. 但是,一些最新的面向?qū)ο笳Z(yǔ)言則采取了把subtyping和subclassing分開的方法。也就是說(shuō),A是B的子類,但A類的對(duì)象卻不可以當(dāng)作B類的對(duì)象來(lái)使用。(譯者按:有點(diǎn)象C++里的私有繼承,但內(nèi)容比它豐富)
  好吧,關(guān)于區(qū)分subclassing和subtyping, 我們后面會(huì)講到。
  下面,讓我們重新回頭來(lái)看看這個(gè)procedure f. 在subsumption的情況下,下面這個(gè)代碼的動(dòng)態(tài)語(yǔ)義是什么呢?
  Procedure f(x: InstanceTypeOf(cell)) is
  x.set(3);
  end;
  f(myReCell);
  當(dāng)myReCell被當(dāng)作InstanceTypeOf(cell)的對(duì)象傳入f的時(shí)候,x.set(3)究竟是調(diào)用哪一個(gè)版本的set方法呢?是定義在cell中的那個(gè)set還是定義在reCell中的那個(gè)呢?
  這時(shí),我們有兩種選擇,
  1. Static dispatch (按照編譯時(shí)的類型來(lái)決定)
  2. Dynamic dispatch (按照對(duì)象運(yùn)行時(shí)真正類型來(lái)決定)
  (譯者按,熟悉C++的朋友們一定微笑了,這再簡(jiǎn)單不過了。)
  static dispatch沒什么可說(shuō)的。
  dynamic dispatch卻有一個(gè)有趣的屬性。那就是,subsumption一定不能影響對(duì)象的狀態(tài)。如果你在subsumption的時(shí)候,改變了這個(gè)對(duì)象的狀態(tài),比如象C++中的對(duì)象切片,那么動(dòng)態(tài)解析的方法就可能會(huì)失敗。
  好在,這個(gè)屬性無(wú)論對(duì)語(yǔ)義,還是對(duì)效率,都是很有好處的。
  (譯者按,C++中的object slicing會(huì)把新的對(duì)象的vptr初始化成它自己類型的vtable指針, 所以不存在動(dòng)態(tài)解析的問題。但實(shí)際上,對(duì)象切片根本不能叫做subsumption。
  具體語(yǔ)言實(shí)現(xiàn)中,如C++, 雖然subsumption不會(huì)改變對(duì)象內(nèi)部的狀態(tài),但指針的值卻是可能會(huì)變化的。這也是一個(gè)讓人討厭的東西,但 C++ vtable的方案卻只能這樣。有一種變種的vtable方法,可以避免指針的變化,也更高效。我們會(huì)在另外的文章中闡述這種方法。)
  賽翁失馬 (關(guān)于類型信息)
  雖然subsumption并不改變對(duì)象的狀態(tài),在一些語(yǔ)言里(如Java), 它甚至沒有任何運(yùn)行時(shí)開銷。但是,它卻使我們丟掉了一些靜態(tài)的類型信息。
  比如說(shuō),我們有一個(gè)類型InstanceTypeOf(Object), 而Object類里沒有定義任何屬性和方法。我們又有一個(gè)類MyObject, 它繼承自O(shè)bject。那么當(dāng)我們把MyObject的對(duì)象當(dāng)作InstanceTypeOf(Object)類型來(lái)處理的時(shí)候,我們就得到了一個(gè)什么東西也沒有的沒用的空對(duì)象。
  當(dāng)然,如果我們考慮一個(gè)不那么極端的情況,比如說(shuō),Object類里面定義了一個(gè)方法f, 而MyObject對(duì)方法f做了重載,那么, 通過dynamic dispatch, 我們還是可以間接地操作MyObject中的屬性和方法的。這也是面向?qū)ο笤O(shè)計(jì)和編程的典型方法。
  從一個(gè)purist的角度看(譯者按,很不幸,我就是一個(gè)purist), dynamic dispatch是你應(yīng)該用來(lái)操作已經(jīng)被subsumption忘掉的屬性和方法的東西。它優(yōu)雅,安全,所有的榮耀都?xì)w于dynamic dispatch。!
  不過,讓purist們失望的是,大部分語(yǔ)言還是提供了一些在運(yùn)行時(shí)檢查對(duì)象類型,并從而操作被subsumption遺忘的屬性和方法。這種方法一般被叫做RTTI(Run Time Type Identification)。如C++中的dynamic_cast, 或Java中的instanceof.
  實(shí)事求是地說(shuō),RTTI是有用的。(譯者按,典型的存在就是合理的強(qiáng)盜邏輯,氣死我了。。但因?yàn)橐恍├碚撋弦约胺椒ㄕ撋系脑,它被認(rèn)為是破壞了面向?qū)ο蟮募儩嵭浴?br>  首先,它破壞了抽象,使一些本來(lái)不應(yīng)該被使用的方法和屬性被不正確地使用。
  其次,因?yàn)檫\(yùn)行時(shí)類型的不確定性,它有效地把程序變得更脆弱。
  第三點(diǎn),也許是最重要的一點(diǎn),它使你的程序缺乏擴(kuò)展性。當(dāng)你加入了一個(gè)新的類型時(shí),你也許需要仔細(xì)閱讀你的dynamic_cast或instanceof的代碼,必要時(shí)改動(dòng)它們,以保證這個(gè)新的類型的加入不會(huì)導(dǎo)致問題。
  很多人一提到RTTI, 總是側(cè)重于它的運(yùn)行時(shí)的開銷。但是,相比于方法論上的缺點(diǎn),這點(diǎn)運(yùn)行時(shí)的開銷真是無(wú)足輕重的。
  而在purist的框架中(譯者按,吸一口氣,目視遠(yuǎn)方,做深沉狀),新的子類的加入并不需要改動(dòng)已有的代碼。
  這是一個(gè)非常好的優(yōu)點(diǎn),尤其是當(dāng)你并不擁有全部源代碼時(shí)。
  總的來(lái)說(shuō),雖然RTTI (也叫type case)似乎是不可避免的一種特性,但因?yàn)樗姆椒ㄕ撋系囊恍┤秉c(diǎn),它必須被非常謹(jǐn)慎的使用。今天面向?qū)ο笳Z(yǔ)言的類型系統(tǒng)中的很多東西就是產(chǎn)生于避免RTTI的各種努力。
  比如有些復(fù)雜的類型系統(tǒng)中可以在參數(shù)和返回值上使用Self類型來(lái)避免RTTI. 這點(diǎn)我們后面會(huì)介紹到。
  協(xié)變,反協(xié)變和壓根兒不變 (Covarance, Contravariance and Invariance)
  在下面的幾個(gè)小節(jié)里,我們來(lái)介紹一種避免RTTI的類型技術(shù)。在此之前,我們先來(lái)介紹“協(xié)變”,“反協(xié)變”和“壓根兒不變”的概念。
  協(xié)變
  首先,讓我們來(lái)看一個(gè)Pair類型: A*B
  這個(gè)類型支持一個(gè)getA()的操作以返回這個(gè)Pair中的A元素。
  給定一個(gè)A’ <: A, 那么,我們可以說(shuō)A’*B <: A*B。
  為什么呢?我們可以用Subsumption的屬性加以證明:
  假設(shè)我們有一個(gè)A’*B類型的對(duì)象a’*b, 這里,a’:A’, b:B, a’*b <: A’*B
  那么,因?yàn),A’ <: A, 從subsumption, 我們可以知道a’:A, getA():A 所以, a’*b<: A*B
  這樣,我們就定義A*B這個(gè)類型對(duì)于A是協(xié)變的。
  同理,我們也可以證明A*B對(duì)于B也是協(xié)變的。
  正規(guī)一點(diǎn)說(shuō),Covariance是這樣定義的:
  給定L(T), 這里,類型L是通過類型T組合成的。那么,
  如果 T1 <: T2 能夠推出 L(T1) <: L(T2), 那么我們就說(shuō)L是對(duì)T協(xié)變的。
  反協(xié)變
  請(qǐng)看一個(gè)函數(shù): A f(B b); (用functional language 的定義也許更簡(jiǎn)潔, 即f: B->A)
  那么,給定一個(gè)B’ <: B, 在B->A 和 B’->A之間有什么樣的subtype關(guān)系呢?
  可以證明,B->A <: B’->A 。
  基于篇幅,我們不再做推導(dǎo)。
  所以,函數(shù)的參數(shù)類型是反協(xié)變的。
  Contravariance的正規(guī)點(diǎn)的定義是這樣的:
  給定L(T), 這里,類型L是通過類型T組合成的。那么,
  如果 T1 <: T2 能夠推出 L(T2) <: L(T1), 那么我們就說(shuō)L是對(duì)T反協(xié)變的。
  同樣,可以證明,函數(shù)的返回類型是協(xié)變的。
  壓根兒不變
  那么我們?cè)倏紤]函數(shù)g: A->A
  這里,A既出現(xiàn)在參數(shù)的位置,又出現(xiàn)在返回的位置,可以證明,它既不是協(xié)變的,也不是反協(xié)變的。
  對(duì)于這種既不是協(xié)變的,也不是反協(xié)變的情況,我們稱之為Invariance (譯者按:“壓根兒不變”是我編的,這么老土的翻譯,各位不必當(dāng)真)
  值得注意的是,對(duì)于第一個(gè)例子中的Pair類型,如果我們支持setA(A), 那么,Pair就變成Invariance了。
  方法特化 (Method Specialization)
  在我們前面對(duì)subclass的討論中,我們采取了一種最簡(jiǎn)單的override的規(guī)則,那就是,overriding的方法必須和overriden的方法有相同的signature.
  但是,從類型安全的角度來(lái)說(shuō),這并不是必須的。
  這樣,只要A <: A’, B’ <: B, 下面的代碼就是合法的:
  class c is
  method m(x:A):B is … end;
  method m1(x1:A1):B1 is … end;
  end;
  subclass c’ of c is.