• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-341  評論-2670  文章-0  trackbacks-0

            在所有的文字之前,我需要強調一下,我本人對structure typing持反對態度,所以就算文中的內容“看起來很像”go的interface,讀者們也最好不要覺得我是在贊揚go的interface。我比較喜歡的是haskell和rust的那種手法。可惜rust跟go一樣恨不得把所有的單詞都縮成最短,結果代碼寫出來連可讀性都沒有了,單詞都變成了符號。如果rust把那亂七八糟的指針設計和go的那種屎縮寫一起干掉的話,我一定會很喜歡rust的。同理,COM這個東西設計得真是太他媽正確了,簡直就是學習面向對象手法的最佳范例,可惜COM在C++下面操作起來有點傻逼,于是很多人看見這個東西就呵呵呵了。

            上一篇文章說這次要寫類成員函數和lambda的東西,不過我想了想,還是先把OO放前面,這樣順序才對。

            我記得我在讀中學的時候經常聽到的宣傳,是面向對象的做法非常符合人類的思維習慣,所以人人喜歡,大行其道,有助于寫出魯棒性強的程序。如今已經過了十幾年了,我發現網上再也沒有這樣的言論了,但是也沒有跟反C++的浪潮一樣拼命說面向對象這里不好那里不好要廢除——明顯人們還是覺得帶面向對象的語言用起來還是比較爽的,不然也就沒有那么多人去研究,一個特別合用來寫functional programming的語言——javascript——是如何可以“模擬”面向對象語言里面的常用操作——new、繼承和虛函數覆蓋了。

            所以像面向對象這種定義特別簡單的東西,語法上應該做不出什么坑的了。那今天的坑是什么呢?答案:是人。

            動態類型語言里面的面向對象說實話我也不知道究竟好在哪里,對于這種語言那來講,只要做好functional programming的那部分,剩下的OO究竟要不要,純粹是一個語法糖的問題。在動態類型語言里面,一個類和一個lambda expression的差別其實不大。

            那么靜態類型語言里面的面向對象要怎么看待呢?首先我們要想到的一個是,凡是面向對象的語言都支持interface。C++雖然沒有直接支持,但是他有多重繼承,我們只需要寫出一個純虛類出來,就可以當interface用了。

            在這里我不得不說一下C++的純虛類和interface的這個東西。假設一下我們有下面的C#代碼:

            interface IButton{}
            interface ISelectableButton : IButton{}
            interface IDropdownButton : IButton{}
            class CheckBox : ISelectableButton{}
            
            class MyPowerfulButton : CheckBox, IDropdownButton
            {
                // 在這里我們只需要實現IDropdownButton里面比IButton多出來的那部分函數就夠了。
            }

            我們先不管GUI是不是真的能這么寫,我們就看看這個繼承關系就好了。這是一個簡單到不能再簡單的例子。意思就是我有兩種button的接口,我從一個實現里面擴展出一個支持另一種button接口的東西。但是大家都知道,我那個完美的GacUI用的是C++,那么在C++下面會遇到什么問題呢:

            #region 抱怨

            一般來說在C++里面用純虛類來代替interface的時候,我們繼承一個interface用的都是virtual繼承。為什么呢?看上面那個例子,ISelectableButton繼承自IButton,IDropdownButton繼承自IButton。那么當你寫一個MyPowerfulButton的時候,你希望那兩個接口里面各自的IButton是不一樣的東西嗎?這當然不是。那如何讓兩個接口的IButton指向的是同一個東西呢?當然就是用virtual繼承了。

            好了,現在我們有CheckBox這個實現了ISelectableButton(帶IButton)的類了,然后我們開始寫MyPowerfulButton。會發生什么事情呢?

            猜錯了!答案是,其實我們可以寫,但是Visual C++(gcc什么的你們自己玩玩就好了)會給我們一個warning,大意就是你IDropdownButton里面的IButton被CheckBox給覆蓋了,再說抽象一點就是一個父類覆蓋了另一個父類的虛函數。這跟virtual繼承是沒關系的,你怎么繼承都會出這個問題。

            但這其實也怪不了編譯器,本來在其他情況下,虛函數這么覆蓋自然是不好的,誰讓C++沒有interface這個概念呢。但是GUI經常會碰到這種東西,所以我只好無可奈何地在這些地方用#pragma來supress掉這個warning,反正我知道我自己在干什么。

            C++沒有interface的抱怨到這里就完了,但是virtual繼承的事情到這里還沒完。我再舉一個例子:

            class A
            {
            private:
                int i;
            public:
                A(int _i)i:(_i){}
            };
            
            class B : public virtual A
            {
            public:
                B(int _i):A(_i){}
            };
            
            class C : public virtual A
            {
            public:
                C(int _i):A(_i){}
            };
            
            class D : public B, public C
            {
            public:
                D():B(1), C(2){}
            };

            大家都是知道什么是virtual繼承的,就是像上面這個例子,D里面只有一個A對象,B和C在D里面共享了A。那么,我們給B和C用了不同的參數來構造,難道一個A對象可以用不同的參數構造兩次嗎,還是說編譯器幫我們隨便挑了一個?

            呵呵呵呵呵呵呵呵,我覺得C++的virtual繼承就是這里非常反直覺——但是它的解決方法是合理的。反正C++編譯器也不知道究竟要讓B還是C來初始化A,所以你為了讓Visual C++編譯通過,你需要做的事情是:

            D()
                : A(0)  // 參數當然是胡扯的,我只是想說,你在D里面需要顯式地給A構造函數的參數
                , B(1)
                , C(2)
            {
            }

            #endregion

            大家估計就又開始吵了,C++干嘛要支持多重繼承和virtual繼承這兩個傻逼東西呢?我在想,對于一個沒有內建interface機制的語言,你要是沒有多重繼承和virtual繼承,那用起來就跟傻逼一樣,根本發揮不了靜態類型語言的優勢——讓interface當contract。當然,我基本上用多重繼承和virtual繼承也是用來代替interface的,不會用來做羞恥play的。

            當我們在程序里面拿到一個interface也好,拿到一個class也好,究竟這代表了一種什么樣的精神呢?interface和class的功能其實是很相似的

            interface IA:只要你拿到了一個IA,你就可以對她做很多很多的事情了,當然僅限大括號里面的!
            class C : IA, IB:只要你拿到了一個C——哦不,你只能拿到interface不能拿到class的——反正意思就是,你可以對她做對IA和IB都可以做的事情了!

            所以contract這個概念是很容易理解的,就是只要你跟她達成了contract,你就可以對她做這樣那樣的事情了。所以當一個函數返回給你一個interface的時候,他告訴你的是,函數運行完了你就可以做這樣那樣的事情。當一個函數需要一個interface的時候,他告訴你的是,你得想辦法讓我(函數)干這樣那樣的事情,我才會干活。

            那class呢?class使用來實現interface的,不是給你直接用的。當然這是一個很理想的情況,可惜現在的語言糖不夠甜,堅持這么做的話實在是太麻煩了,所以只好把某些class也直接拿來用了,GUI的控件也只好叫Control而不是IControl了。

            其實說到底class和interface有什么區別呢?我們知道面向對象的一大特征就是封裝,封裝的意思就是封裝狀態。什么是狀態呢?反正云風一直在說的“類里面的數據”就不是狀態。我們先來看什么是數據:

            struct Point
            {
                int x;
                int y;
            };

            這就是典型的數據,你往x和y里面隨便寫什么東西都是沒問題的,反正那只是一個點。那什么是狀態呢:

            struct String
            {
                wchar_t* buffer;
                int length;
            };

            String和Point有什么不一樣呢?區別只有一個:String的成員變量之間是滿足一個不變量的:wcslen(buffer) == length;

            如果我們真的決定要給String加上這么個不變量的話,那這里面包含了兩點:
            1:buffer永遠不是nullptr,所以他總是可以被wcslen(buffer)
            2:length的值和buffer有直接的關系

            如果你要表達一個空字符串,你總是可以寫buffer=L””,不過這就要你給String再加上一些數據來指明這個buffer需要如何被釋放了,不過這是題外話了。我們可以假設buffer永遠是new[]出來的——反正這里不關心它怎么釋放。

            這個不變量代表什么呢?意思就是說,無論你怎么折騰String,無論你怎么創建釋放String,這個等式是一定要滿足的。也就是說,作為String外部的“操作人員”,你應當沒機會“觀測”到這個String處于一個不滿足不變量的狀態。

            所以這兩個成員變量都不應該是public的。因為哪怕你public了他們其中的一個,你也會因為外部可以隨意修改它而使他進入一個不滿足不變量的狀態。

            這代表了,為了操作這些成員變量,我們需要public一些函數來給大家用。其實這也是contract,String的成員函數告訴我們,你可以對我(String)做很多很多的事情哦!

            這同時也代表了,我們需要一個構造函數。因為如果我們在創建一個String之后,實例沒有被正確初始化,那么他就處于了一個不滿足不變量的狀態,這就不滿足上面說的東西了。有些人喜歡帶一個Init函數和一個基本不干什么事情的構造函數,我想說的是,反正你構造完了不Init都不能用,你為什么非要我每次創建它的時候都立刻調用Init這么多次一舉呢?而且你這樣會使得我無法對于一個這樣的函數f(shared_ptr<ClassThatNeedsInit> x)直接寫f(make_shared(new ClassThatNeedInit))因為你的構造函數是殘廢的!

            有些人會說,init沒有返回值,我不知道他犯了錯誤啊——你可以用Exception

            還有些人會說,exception safe的構造函數好難寫啊——學啊我艸

            但是這樣仍然有些人會負隅頑抗,都這么麻煩了反正我可以用對Init和返回值就好了——你連exception safe的構造函數都不知道怎么寫你怎么知道你可以“用對”它們

            #region 題外話展開

            但是有些人就喜歡返回error,怎么辦呢?其實我們都很討厭Java那個checked exception的對吧,要拋什么exception都得在函數簽名里面寫,多麻煩啊。其實這跟error是一樣的。一個exception是可以帶有很多豐富的信息的——譬如說他的callstack什么的,還可以根據需要有很多其他的信息,總之不是一個int可以表達的。這就是為什么exception【通常】都是一個類。那如果我們不能用exception,但是也要返回一樣多的信息怎么辦?你只好把函數的返回值寫得相當的復雜,譬如說:

            struct ErrorInfoForThisFunction
            {
                xxxxxxxx
            };
            
            template<typename R, typename E>
            struct ReturnValue // C++沒有好用的tuple就是臥槽
            {
                bool hasError;
                R returnValue;
                E errorInfo;
            };
            
            ReturnValue<ReturnType, ErrorInfoForThisFunction> ThisFunction( ... ); //我知道因為信息實在太多你們又要糾結返回struct還是它的指針還是ReturnValue里面的東西用指針還是用引用參數等等各種亂七八糟的事情了哈哈哈哈哈哈

            于是現在出問題了,我有一個ThatFunction調用ThisFunction,當錯誤是一種原因的時候我可以處理,當錯誤是另一種原因的時候我無法處理,所以在這種情況下我有兩個選擇:
            1:把錯誤信息原封不斷的返回
            2:把ThisFunction的錯誤信息包裝成ThatFunction的錯誤信息
            不過我們知道其實這兩種方法都一樣,所以我們采用第一種:

            struct ErrorInfoForThatFunction
            {
                yyyyyyyy
            };
            
            ReturnValue<ReturnType2, tuple<ErrorInfoForThisFunction, ErrorForThatFunctio, bool /*用來代表哪個是有效的*/> ThatFunction( ...  ); //數據越來越多我知道你們會對返回值糾結的越厲害

            你可能會說,為什么不把tuple包裝成另一個struct?其實都一樣,懶得寫了。

            我們知道,通常一個常見的幾百人一起寫的小軟件都會有幾百上千萬行甚至幾十G代碼的,函數的調用層次只有幾十層都已經很不錯了。就算調用鏈里面只有10%的函數添加了自己的錯誤信息,那累積到最后肯定會很壯觀的。而且只要底層的一個函數修改了錯誤信息,所有直接間接調用它的函數都會受到影響。

            這簡直就跟Java的checked exception一樣嘛!

            有些人會說,我們有error code就夠了!我知道你們根本沒有好好想“怎么做error recovery”這件事情

            有些人還會說(就在我微博上看見的),用error code就是代表可以不處理,我干嘛要費那么多心思搞你這些返回值的東西?我對這種人只能呵呵呵了,轉行吧……

            這個時候我就會想,C++多好啊,我只要把ReturnValue<ReturnType, ErrorInfoForThisFunction>給改成ReturnType,然后在函數里面發生了錯誤還是構造一個ErrorInfoForThisFunction,然后直接給throw出來就好了。throw一個值我們還不用關心怎么釋放它,多省事。對于ErrorInfoForThatFunction,我還可以讓這兩個struct都繼承自同一個基struct(就是你們經常在別的語言里面看見的Exception類了),這樣我在外面還可以直接catch(const 基struct& ex)。

            有些人會說,為什么不強制所有繼承都繼承自Exception?我知道你們就只是想catch了之后不理罷了,反正C++也有catch(…)你們偷著樂就行了

            用Exception有性能問題?反正在不發生錯誤的情況下,寫了幾句try也就只是操作了寫在FS : [ 0 ]里面的一個鏈表而已,復制幾個指針根本就不算什么影響

            C++的catch不能抓到Access Violation(也就是segmant fault?)?現在連最新的.net你來寫catch(Exception ex)也抓不到AccessViolationException了。都AV了你的內存都搞得一團糟了,如果你這個時候還不備份數據dump自己然后退出重啟(如果需要的話),那你接著執行代碼,天知道會發生什么事情啊!連C#都覺得這么做危險了,C++只能更危險——所以用SEH抓下來dump自己然后進程自殺吧。Java還區分Error和Exception,雖然我不知道他具體代表什么,反正一般來說Exception有兩種
            1:可以預見的錯誤,譬如說Socket斷開了所以Write就是敗了給個Exception之類的
            2:必須修改代碼的錯誤,譬如說數組下標越界——這除了你寫錯以外根本沒有別的原因,就應該掛掉,這時候你debug的時候才能立刻知道,然后改代碼

            所以有三個基類然后最嚴重的那種不能catch我覺得也是一種好的選擇。你可能會問,那C#在AV之后你又抓不到那怎么知道呢?答案:Application類有一個事件就是在發生這類事情的時候被調用的,在里面dump就好了。如果你非要抓AV,那也可以抓得到,就是有點麻煩……

            #endregion

            說了這么多,無非就是因為一個類的構造函數——其實他真的是一個函數,只是函數名和類名一樣了,這種事情在js里面反正經常出現——強制了你只能返回正確的時候的結果,于是有些人沒辦法加入error code了,又不知道怎么正確使用exception,只好搞出個C語言的封建社會殘留思想Init函數來。其實我們知道,一旦有了Exception,函數簽名里面的返回值就是他正確執行的時候返回的東西,這根構造函數一樣。

            C++的exception在構造函數里面不好,其實是因為一旦構造函數發生了異常,那代表這個類沒構造完,所以析構函數是不會執行的。這在一定程度上給你寫一個正確的構造函數(這也是“如何寫一個正確的類”的其中一個方面)帶來了麻煩,所以很多人到這里就呵呵呵了。

            這就跟很多人學習SQL語言結果到group by這里就怎樣也跨不過去了一樣——人和人之間說沒有差距這個不符合客觀現實啊……

            不過我不否認,想寫一個正確的C++程序是一件非常困難的事情,以至于連我在【只有我自己用的那部分library】里面都不是總是遵守各種各樣的規則,反正我寫的代碼,我知道怎么用。不過公司的代碼都是一大堆人一起寫的,就像sqlserver一個組有一千多人一樣(oracle是我們的十幾倍,我們還能活下來真是太不容易了)——你能每個人都溝通到嗎?撐死了就幾十個吧,才不到10%。天知道別人會在代碼里面干什么。所以寫代碼是不能太隨便的。同理,招人也不能太隨便,特別是你們這些連code review都不做的公司,你平時都不能阻止他checkin垃圾代碼,你還敢招不合格的人嗎

            現在我們回到面向對象的東西。Exception其實也應該算在contract里面,所以其實interface里面的函數會拋什么異常是需要明確的表達出來的。但是checked exception這個東西實在是太蠢了,因為這個規則是不能組合的,會導致上面說的error返回值一樣的“接口信息大爆炸”。

            所有不能組合的東西都是很難用的,譬如checked exception,譬如鎖,譬如第一篇文章說的C語言那個碉堡了的函數指針數組作為參數的一個成員函數指針類型的聲明什么的。

            如果你不直接寫出這個函數會拋exception,那要怎么辦呢?方法有兩個:
            1:你給我把文檔寫好了,而且你,還有你,用這個library之前,給我RTFM!
            2:就跟VisualStudio一樣支持xml注釋,這樣VS就可以在你調用這個函數的時候用tooltip的形式提示你,你需要注意這些那些事情……

            什么?你不用IDE?給我RTFM!你連文檔都不看?滾!明天不要來上班了!

            突然發現本來要寫面向對象的,結果Exception也寫了相當長的一段。這件故事告訴我們,就算你不知道interface as contract是什么意思,你還能湊合寫點能維護的代碼。但是你Exception用得不好,程序就寫成了渣,這個問題比較嚴重,所以寫的也就比較多了。所以下面我們真正來談contract的事情。需要注意的是,C++對這種東西是用你們很討厭的東西來支持的——模板和偏特化。

            contract的概念是很廣泛的。對于面向對象語言來說,int這種東西其實也可以是一個類。你們不要老是想著編譯后生成什么代碼的事情,語法這種東西只是障眼法而已,編譯出來的東西跟你們看到的可以是完全不同的。一個典型的例子就是尾遞歸優化了。還有C#的int雖然繼承自object但是你直接用他的話生成出來的代碼跟C++是沒什么區別的——因為編譯器什么后門都可以開!

            那我們就拿int來說吧。int有一個很重要的特征,就是可以用來比較。C++怎么表達這個事情的呢?

            struct int
            {
                ......
                bool operator<(int i);
                ......
            };

            如果你想寫一個排序函數,內部想用<來排序的話,你不需要在接口上寫任何東西,你只需要假設那個typename T的T可以被<就好了。所有帶有<的類型都可以被這個函數使用。這特別的structure typing,而且C++沒有concept mapping,導致了你無法在接口上表達“這個類必須帶<”的這件事情,所以一旦你用錯了,這錯誤信息只能跟煙霧一般繚繞了……

            concept mapping其實也是一個面向對象的特別好用特征不過這太高級了估計很多人都沒用過——你們又不喜歡haskell和rust——那對于我們熟悉的面向對象的特性來講,這樣的事情要怎么表達呢?

            于是我們偉大的先知Anders Hejlsberg菊苣就做了這么個決定(不是他干的也是他手下干的!)

            interface IComparable // .net 1.0沒有模板,只好搞了這么個傻逼東西出來
            {
                int CompareTo(object o); //具體的忘了,這是我根據記憶隨便寫的
            }
            
            interface IComparable<T>
            {
                int CompareTo(T o);
            }
            
            struct Int32 : IComparable, IComarable<T> ...
            {
                ......
            }

            所以你的排序函數只需要寫成:

            void Sort<T>(T[] data)
                where T : IComparable<T>
            { ...... }

            看看IComparable<int>這個傻逼。我為什么要創建一個對象(IComparable<int>),他的職責就是跟另一個int作比較?這實在是太蠢了,無論怎么想都不能想出這種對象到底有什么存在的意義。不過因為C#沒有concept mapping,于是看在interface as contract的份上,讓interface來干這種事情其實也是很合理的。

            所以contract這個東西又賦予了一個更加清晰的意義了。我們其實想要表達的事情是“我們可以對這個參數做什么事情”,而不是“這個參數是什么類型”。所以在這個Sort函數里面,這個T其實不代表任何事情,真正起到聲明的作用的是where T : IComparable<T>這一句,指明了data數組里面的所有東西都是可以被排序的。那你可能會問,為什么不把IComparable<T>改成IComparable然后干脆把參數改成IComparable[] data呢?雖然說這樣做的確更加“面向對象”,但是實在是太不現實了……

            本來面向對象這種概念就不是特別的可以表達客觀現實,所以出現一些這種狀況,也是正常的。

            #region 題外話

            看看這兩個函數:

            void Sort<T>(T[] data) where T:IComparable<T>;
            void Sort(IComparable[] data);

            他們互相之間存在了一個特別優美的(數學意義上的)變換,發現沒有,發現沒有!所以對于動態類型語言(interface可以從代碼里面得到)做一些“靜態化”的優化的時候,就可以利用類似的技巧——咳咳,說太遠了,趕緊打住。誰說懂點代數對編程沒用的?哼!

            #endregion

            在這里我們終于看到了contract在帶泛型的純潔的面向對象語言里面的兩種表達方法。你可能會想,我想在java里面干這種事情怎么辦?還是換C#吧。那我們拿到一個class的時候,這代表什么呢?其實我們應該看成,其實我們拿到的是一個interface,只是他恰好只有一個實現。所以在這種時候,你最好不要依賴于“這個interface恰好只有一種實現而且我知道他是什么”的這個事情,否則程序寫大了,你會發現你越來越不滿足“面向interface編程”的這個原則,代碼越來越難處理了。

            我們可能會想到另一件事情,先知們跟我們說,當你設計函數參數的類型的時候,這個類型越基,哦不,越是在繼承鏈里面距離object靠得越近越好,這是為什么呢?這其實也是一個interface as contract的問題。舉個例子,我們需要對一個數組求和:

            T Sum<T>(T[] data, Func<T, T, T> 加法函數); // C#真的可以用中文變量的哦!

            費盡心思終于寫好了,然后我們第二天發現,我們對List<T>也要做一樣的事情。那怎么辦呢?

            在這里,Sum究竟對data有什么需求呢?其實研究一下就會發現,我們需要的只是想遍歷data里面的所有內容而已。那data是不是一個數組,還是列表,還是一顆二叉樹,還是你垃圾ipad里面的一些莫名其妙的數據也好,其實都一樣。那C#里面什么interface代表“遍歷”這個contract呢?當然是IEnumerable<T>了:

            T Sum<T>(IEnumerable<T> data, Func<T, T, T> 加法函數); // C#真的可以用中文變量的哦!

            這樣你的容器只要能夠被foreach,也就是繼承自IEnumearble<T>,就可以用這個函數了。這也是為什么”linq to 容器”基本上都是在IEnumerable上做的,因為他們只需要遍歷。

            哦,我們還說到了foreach。foreach是一個語句,用來遍歷一個容器。那你如何表達一個容器可以被foreach拿來遍歷的這個contract呢?還是IEnumerable<T>。interface拿來做contract的確是最佳匹配呀。

            其實說了這么多,我只想表達一個東西。不要太在意“這個變量的類型是什么”,你要關心的是“我可以對這個變量做這樣那樣的事情”。這就是為什么我們會推薦“面向接口編程”,因為人總是懶散的,需要一些約束。interface不能用來new,不包含成員變量,剛好是contract的一種很好地表達方法。C++表達contract其實還可以用模板,不過這個就下次再說了。如果你們非得現在就知道到底怎么用的話,我就告訴你們,只要把C#的(i as IComparable<int>).CompareTo(j)換成Comparable<int>::Compare(i, j)就好了。

            所以在可能的情況下,我們設計函數的參數和返回值的類型,也盡量用更基的那些類型。因為如果你跟上面的Sum一樣,只關心遍歷,那么你根本不應該要求你的參數可以被隨機訪問(數組就是這個意思)。

            希望大家看完這篇文章之后可以明白很多我們在面向對象編程的時候,先知們建議的那些條款。當然這里還不涉及設計模式的東西。其實設計模式說白了是語法的補丁。設計模式這種東西用的最多,C#略少,你們會發現像listener模式這種東西C#就不常用,因為它有更好的東西——event。

            posted on 2013-05-04 19:29 陳梓瀚(vczh) 閱讀(15565) 評論(16)  編輯 收藏 引用 所屬分類: 啟示

            評論:
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理)[未登錄] 2013-05-04 19:56 | raof01
            我覺得contract并不僅僅指能做什么事情,還包括做事情前后的狀態應該是什么樣子  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-04 21:03 | silverbullettt
            寫得太好了臥槽  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-04 23:17 | 陳梓瀚(vczh)
            @raof01
            嗯,這個我把它用不變量放在了class的封裝里面去了——但是interface沒有“數據”,所以就不強調這個。  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理)[未登錄] 2013-05-05 00:07 | raof01
            @陳梓瀚(vczh)
            用不變量來表示狀態碉堡了,呵呵,我以前怎么沒想到。這樣能區分POD和class。云風說沒有狀態估計說的是POD。  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-05 00:35 | 陳梓瀚(vczh)
            @raof01
            游戲嘛,除了script寫邏輯以外,其他的部分業務室是簡單的,云風喜歡那樣也是情有可原。  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-05 02:36 | ccsdu2009
            寫的不錯 支持你  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-05 03:14 | godzza
            1:把錯誤信息原封不斷的返回
            2:把ThisFunction的錯誤信息包裝成ThatFunction的錯誤信息
            不過我們知道其實這兩種方法都一樣,所以我們采用第一種:
            ---------------------------------------
            1:把錯誤信息原封不動的返回
            2:把ThisFunction的錯誤信息包裝成ThatFunction的錯誤信息
            不過我們知道其實這兩種方法都一樣,所以我們采用第二種:

            貌似是這樣?  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-05 03:45 | sorra
            void Sort<T>(T[] data) where T:IComparable<T>;

            Java: <T extends Comparable<T>> void sort(T[] data);

            感覺差不多吧  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-05 05:46 | 陳梓瀚(vczh)
            @godzza
            2:是第一種,原封不斷的返回(到了tuple里面)  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-05 07:10 | 溪流
            學習了  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-05 07:34 | yrj
            From wikipedia

            Concept (generic programming)
            http://en.wikipedia.org/wiki/Concept_(generic_programming)

            As generics in Java and C# have some similarities to C++'s templates, the role of concepts there is played by interfaces. However there is one important difference between concepts and interfaces: when a template parameter is required to implement a particular interface, the matching type can only be a class that implements (explicitly) that interface. Concepts bring more flexibility because they can be satisfied by two ways:
            - explicitly defined as satisfied by using a concept map (defined separately to the type itself, unlike interfaces)
            - implicitly defined for "auto concepts", which can be used also for built in types and other types that were not predestined for this use  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-05 19:27 | 永遇樂
            等C++語法支持concept了,就能更自然地造出interface了  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理)[未登錄] 2013-05-07 05:57 | 唐風
            異常處理這段深有同感。

            現在做的系統中流用了大量的C代碼,全部是error-check方式。

            只能說,用error-check大部情況下只是“掩耳盜鈴”而已,Check一下函數出錯了就打個屏,完全沒有想過是否可能恢復,如何恢復,也沒有辦法想,error-code根本就傳不出去。

            所以軟件“品質”就成了“測出來的”,而不是設計出來的了,因為設計的時候根本就無法處理這么復雜的層次的函數調用帶來的錯誤碼,而且中間只要有人不傳遞就完蛋了。
              回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理)[未登錄] 2013-05-07 06:01 | 唐風
            另,正在做提案去error-code化,加exception的體系設計。
            但是做嵌入式,大家都很敏感,所以還是要先把現在所用的平臺和編譯環境中,exception空間和時間開銷分析清楚給客戶。目前還在整,感覺時間開銷上完全沒有問題(特別是normal case中,異常時其實慢一點也無所謂了,因為設計中的異常真的是非常非常地異常!),但代碼膨脹還是有點點擔心(因為存儲空間比較有限)。不知道 MS 是不是有這方面的數據?特別是WinCE500方面……  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-05-12 00:35 | 陳梓瀚(vczh)
            @唐風
            WinCE的我還真不知道,不過我想這應該是編譯器的問題吧。  回復  更多評論
              
            # re: 如何設計一門語言(三)&mdash;&mdash;什么是坑(面向對象和異常處理) 2013-10-25 21:24 | lichray
            // C++沒有好用的tuple就是臥槽

            Try Boost.Fusion.  回復  更多評論
              
            精品久久久久久国产三级| 亚洲午夜久久久久久久久久| 日产精品久久久一区二区| 久久99精品国产麻豆| 久久精品国产秦先生| 思思久久99热免费精品6| 欧美精品久久久久久久自慰| 久久中文娱乐网| 久久久久久国产精品美女| 精品午夜久久福利大片| 亚洲天堂久久久| 欧美777精品久久久久网| 久久久久久久久波多野高潮| 伊人色综合久久| 国产精品美女久久久久久2018| 亚洲欧美国产精品专区久久| 国产亚洲婷婷香蕉久久精品| 国产A三级久久精品| 精品国产91久久久久久久a| 久久精品国产亚洲av影院| 国内精品久久久久影院亚洲| 亚洲一本综合久久| 国产精品久久网| 久久国产精品成人片免费| 一本色道久久88—综合亚洲精品| 成人国内精品久久久久影院VR| 久久久久久午夜成人影院| 国产亚洲精久久久久久无码77777 国产亚洲精品久久久久秋霞 | 久久影视综合亚洲| 国产69精品久久久久99尤物| 国产精品久久波多野结衣| 久久亚洲春色中文字幕久久久 | 久久精品国产亚洲综合色| 99久久精品免费看国产一区二区三区 | 久久无码一区二区三区少妇| 久久久国产精品福利免费| 久久久久久久人妻无码中文字幕爆| 2021国内久久精品| 伊人色综合久久天天人手人婷 | 亚洲愉拍99热成人精品热久久| 武侠古典久久婷婷狼人伊人|