亚洲免费乱码视频,日韩 欧美 国产 动漫 一区,97在线观看免费视频播国产,中文字幕亚洲图片

      1. <legend id="ppnor"></legend>

      2. 
        
        <sup id="ppnor"><input id="ppnor"></input></sup>
        <s id="ppnor"></s>

        計算機二級C++輔導:面向?qū)ο笳Z言概論(二)

        字號:

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