• <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>
            隨筆-90  評論-947  文章-0  trackbacks-0

            如果要 typedef,搞出來的都是些很惡心的名字,自己看了也生氣,還是不搞好了,就 size_t 和 int 玩到底吧。

            把 Array 的接口又改得一塌糊涂了,重新貼一下:

            namespace xl
            {
               
            template <typename ValueType>
               
            class Array
               
            {
               
            public:
                   
            Array(size_t nSize = 0);
                   
            Array(size_t nSize, const ValueType &tValue);
                   
            Array(const Array<ValueType> &that);
                    ~
            Array();

               
            public:
                   
            class Iterator
                   
            {
                   
            public:
                       
            Iterator();
                       
            Iterator(ValueType *pValue);
                       
            Iterator(ValueType *pValue, ValueType *pStart, ValueType *pEof);
                       
            Iterator(const Iterator &that);

                   
            private:
                       
            ValueType *m_pStart;
                       
            ValueType *m_pEof;
                       
            ValueType *m_pCurrent;

                   
            public:
                       
            ValueType &operator * ();
                       
            ValueType *operator -> ();

                   
            public:
                       
            Iterator &operator = (const Iterator &that);
                       
            bool operator == (const Iterator &that) const;
                       
            bool operator != (const Iterator &that) const;

                   
            public:
                       
            Iterator &operator ++ ();
                       
            Iterator operator ++ (int);
                       
            Iterator &operator -- ();
                       
            Iterator operator -- (int);

                   
            public:
                       
            Iterator operator +(int nDistance) const;
                       
            Iterator operator -(int nDistance) const;
                       
            Iterator &operator +=(int nDistance);
                       
            Iterator &operator -=(int nDistance);
                    };

                   
            class ReverseIterator : public Iterator
                   
            {
                   
            public:
                       
            ReverseIterator &operator ++ ();
                       
            ReverseIterator operator ++ (int);
                       
            ReverseIterator &operator -- ();
                       
            ReverseIterator operator -- (int);
                    };

               
            public:
                   
            Iterator Begin() const;
                   
            Iterator End() const;
                   
            ReverseIterator RBegin() const;
                   
            ReverseIterator REnd() const;


               
            public:
                   
            Array<ValueType> &operator=(const Array<ValueType> &that);
                   
            bool operator==(const Array<ValueType> &that) const;
                   
            bool operator!=(const Array<ValueType> &that) const;

               
            public:
                   
            ValueType &operator[](size_t nIndex);
                   
            const ValueType &operator[](size_t nIndex) const;

               
            public:
                   
            bool Empty();
                   
            size_t Size() const;
                   
            void SetSize(size_t nSize);

               
            public:
                   
            void Insert(const Iterator &itBeforeWhich, const ValueType &tValue);
                   
            void Insert(const ReverseIterator &itBeforeWhich, const ValueType &tValue);
                   
            void PushFront(const ValueType &tValue);
                   
            void PushBack(const ValueType &tValue);
                   
            template <typename IteratorType>
                   
            void Insert(const Iterator &itBeforeWhich, const IteratorType &itFirstToInsert, const IteratorType &itAfterLastToInsert);
                   
            template <typename IteratorType>
                   
            void Insert(const ReverseIterator &itBeforeWhich, const IteratorType &itFirstToInsert, const IteratorType &itAfterLastToInsert);
                   
            Iterator Delete(const Iterator &itWhich);
                   
            ReverseIterator Delete(const ReverseIterator &itWhich);
                   
            void PopFront();
                   
            void PopBack();
                   
            Iterator Delete(const Iterator &itFirstToInsert, const Iterator &itAfterLastToDelete);
                   
            Iterator Delete(const ReverseIterator &itFirstToInsert, const ReverseIterator &itAfterLastToDelete);
                   
            void Clear();
                   
            void SetValue(const Iterator &itWhich, const ValueType &tValue);
                   
            void SetValue(const ReverseIterator &itWhich, const ValueType &tValue);
                   
            void SetValue(const Iterator &itFirstToSet, const Iterator &itAfterLastToSet, const ValueType &tValue);
                   
            void SetValue(const ReverseIterator &itFirstToSet, const ReverseIterator &itAfterLastToSet, const ValueType &tValue);

               
            private:
                   
            ValueType *m_pData;
                   
            size_t m_nSize;
                   
            size_t m_nStart;
                   
            size_t m_nEof;

               
            private:
                   
            void Release();
                   
            size_t GetWellSize(size_t nSize) const;
                   
            void MoveData(size_t nIndex, size_t nCount, int nDistance);
                   
            void CopyData(size_t nIndex, size_t nCount, ValueType *pNewMem) const;

                };
            }

            主要的考慮,還是想實現(xiàn)“跨容器的 iterator”(抱歉,我還是覺得這么稱呼挺符合我的預(yù)期的)。只是,在沒有語言層面的 concepts 支持的情況下,如何顯式地讓用戶知道模板參數(shù)要符合哪些條件呢?

            在這里再次感謝一下 OwnWaterloo 同學(xué),上篇評論里的東西我會繼續(xù)慢慢琢磨的。^_^

            posted on 2009-09-28 23:13 溪流 閱讀(744) 評論(18)  編輯 收藏 引用 所屬分類: C++

            評論:
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 00:28 | OwnWaterloo
            顯示讓用戶知道么……
            有約定俗成
            再不成就文檔
            再不成…… 只能看源代碼了……

            好消息是, 違反concepts一般都錯在編譯時,不會將錯誤留在運行時……
              回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 09:32 | 溪流
            @OwnWaterloo

            如果有個 template <T> where T is IIterator,用戶看接口定義就知道該怎么做了;可是現(xiàn)在,只能到調(diào)用 Iterator 的某個具體方法的時候才報錯,用戶理解這個錯誤,就要理解我的實現(xiàn)了。這不僅增加了學(xué)習(xí)代價,還在一定程度上違背了封裝的初衷,不是么?一點變通的辦法都沒有嗎?
              回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 15:55 | 陳梓瀚(vczh)
            如果你對效率要求不是跟人家開發(fā)SqlServer的后臺一樣高的話,那就來一個虛基類吧,當(dāng)成C#的interface用用。虛基類的作用主要在于你可以用他搞類型計算,譬如說來一個iterator你就可以知道它的element type是什么,而不用跟stl里面的一樣要求你必須在iterator的里面typedef。而且你還可以做很多iterator<A>到iterator<B>的轉(zhuǎn)換,從map<a,b>到iterator<pair<a,b>>的轉(zhuǎn)換等等。就是慢一點點罷了(真的是一點點)。  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 15:56 | 陳梓瀚(vczh)
            @OwnWaterloo
            壞消息是,那些編譯錯誤根本看不懂。  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 15:56 | 陳梓瀚(vczh)
            @陳梓瀚(vczh)
            我寫了一個錯誤的iterator然后錯誤消息竟然是list<T>里面的哪個代碼用了一個不存在的函數(shù)  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 16:09 | 陳梓瀚(vczh)
            @溪流
            template<typename T>
            class IEnumerator
            {
            public:
            virtual int Index()const=0;
            virtual const T& Current()const=0;
            virtual bool MoveNext()const=0;
            virtual void Reset()const=0;
            virtual bool Available()const=0;
            virtual IEnumerator<T>* Clone()const=0;
            };

            template<typename T>
            class IEnumerable
            {
            public:
            virtual IEnumerator<T>* CreateEnumerator()const=0;
            };

            //注意使用方法
            void MyCopy(List<int>& dest, const IEnumerable<int>& source)
            {
            ....
            }  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 16:14 | 陳梓瀚(vczh)
            @溪流
            而且我認(rèn)為作為一個iterator,我絕對不會想到是Array.SetValue來通過它修改自己的值的  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 18:33 | 溪流
            @陳梓瀚(vczh)

            virtual IEnumerator<T>* CreateEnumerator()const=0;

            這個,到使用的時候:
            IEnumerator<T>* p = CreateEnumerator();
            然后求用戶去 delete 嗎?  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 18:33 | 溪流
            @陳梓瀚(vczh)

            嗯……iterator 一般有哪些約定成俗的規(guī)則?  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 18:35 | 溪流
            @陳梓瀚(vczh)

            我覺得,出現(xiàn)在具體實現(xiàn)上的編譯錯誤,本不應(yīng)該留給用戶的。最好是在類型檢查的時候就能報錯。  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 20:23 | OwnWaterloo
            OO為什么被人吹捧?
            可以說, 是它開創(chuàng)了一個歷史,人們開始普遍采用 "對抽象的、多態(tài)的事物編程"。
             
            在那之前, 無論是OP還是OB, 都只能完成模塊化, 方便分工開發(fā)與測試。很少使用多態(tài)技術(shù)。

            OB編程(OP就省了…… OP->OB依然能完成一定復(fù)用,但方式不同):
            void f_ob(T v) { v.f(); }
             
            f是針對一個具體的T進行編程。
            f的行為和T的行為緊緊綁定在一起。
            f想要表現(xiàn)出不同行為, 必須要T表現(xiàn)出不同行為。
            但無論T如何改變,T和f都只具有一種行為,最后一次改變后具有的行為。
             

            OO編程:

            void f_oo(I& i) { i.f(); }
             
            f現(xiàn)在的行為, 僅僅與i所表現(xiàn)出的契約綁定在一起。
            而i可以有各種各樣的滿足契約的方式
            這樣的一大好處就是, 對不同的D : I, f可以被復(fù)用
            得到這一好處的前提就是, D 滿足 I與f之間的契約。
             

            GP編程:
            template<typename T>
            void f_gp(T v) { v.f(); }
             
            同樣提供了多態(tài)行為:以不同的T帶入f, 就能得到不同的行為。
            使得f能被復(fù)用。STL中的組件, 大部分是通過這種方式被復(fù)用的。
            并且,STL組件之間, 也大部分是通過這種方式復(fù)用的。
             

            1.
            無論是GP還是OOP都是組合組件的方式。
            它們(典型地)通過concepts 和 interface來抽象出組件多態(tài)行為。
            對這種抽象事物編程得到的結(jié)果(函數(shù)/類模板,包),可以被復(fù)用(軟件工程一大目標(biāo))。
            -------- -------- -------- -------- -------- -------- -------- --------
             

            上面提到契約。
            無論是OP、OB、OO、GP, 組件間要能協(xié)同工作, 都是需要契約的。
             
            OP:
            free 可以接受空指針, 而printf 不行。
            前者是free對調(diào)用者定下的約束, 后者也是printf對調(diào)用者定下的約束
            —— 要使用我就必須這樣做。
             
            malloc 失敗返回空指針, 而new 拋異常, 否則返回可用動態(tài)內(nèi)存。
            這是它們對調(diào)用者的承諾。
            —— 使用我, 你能得到什么。

            T* p = new T;
            if (!p)  這種代碼只在古董編譯器上才可能有意義。
             
            "加上NULL判斷更好" , 也只有"C++方言"的古董學(xué)者才說得出來。
             
            new[] 要 delete[] , new 要 delete, 這也是契約。
            "因為char是基礎(chǔ)類型,所以可以new[] , delete", 只有不懂軟件契約的白癡學(xué)者才說得出來。
             

            OB:
            要將CString s 轉(zhuǎn)換為 TCHAR*, 一定要用 s.GetBuffer  而不是 (TCHAR*)&s[0]
            CString 的約束。
             
            basic_string.c_str() 一定以'\0'結(jié)尾, 而data() 則不是。
            basic_string  的承諾。
             

            OOP:
            我用的OOP庫、框架不多……  舉不出什么例子。
            但它的契約和通常OB"非常類似" : 完成什么、需要調(diào)用什么、調(diào)用順序、 參數(shù)合法性。
             

            GP:
            GP總共有哪些契約形式, 我總結(jié)不出來。
            但至少有一條 —— 它不再對T有完全限定, 而只作最小限定
             

            還是上面的代碼:
            void f_oo(I& i ) { i.f(); }
            D d;
            f(d); // 要求 D : I

            template<typename T>
            void f_gp(T v) { v.f(); }
            要求  v.f(); 合乎語法 :比如, 它既可以是non-static member function, 也可以是static member function。
            并且僅僅要求這一點
             
             
            2.
            契約是普遍存在的, 不僅僅是GP、 其他范式都有。
            它是合作與復(fù)用的前提。
            -------- -------- -------- -------- -------- -------- -------- --------
             
             
            3.
            為什么GP飽受爭議, 而OO沒有?
            我覺得, 是因為從OB到OO過度比較自然
             
             
            要求一個I需要怎樣, 一個I需要使用者怎樣的時候,
            通常也會有類似的需求 —— 要求一個C怎樣, 一個C需要使用者怎樣。
            注意, 這只是 client 與 I, 以及 client 與 C之間的契約。
            在這個層面上, 不需要學(xué)習(xí)新的思想。
            而下面會提到OO引入的新的契約形式。
             

            而GP的契約形式, 都是一些全新的形式
            其中最主要的形式是: T 是否具有一個叫f的函數(shù)(操作符也可以理解為一種調(diào)用函數(shù)的方式)。
            在C++中, 還有必須有某個名字的嵌套類型, 通過T::f強制static member function等形式。

            OO比較流行、支持OO的語言比較多,是目前主流。
            C++,Java,C#的開發(fā)者總共占了多少比例?
             
            GP支持的語言其實也多。
            但拋開C++(或者算上C++用戶中理解GP的人)怎么都比不上上面3個巨頭。
             

            批評自己不熟悉的事物是不嚴(yán)謹(jǐn)?shù)摹?br>遇見STL編譯錯誤, 要么就去學(xué)習(xí)GP的方式, 要么就拋棄STL。
            抱怨STL是種傻逼行為 —— 明明是自己不會用, 要怪只能怪自己。
             
             
            -------- -------- -------- -------- -------- -------- -------- --------
            4.
            如同GP、 OO同樣需要學(xué)習(xí)、 需要文檔、 否則會充滿陷阱
             
            上面提到的client 與 I的契約形式通常和 clinet 與 C之間的形式相同。
            使得OO的一個方面可以"溫固"。
             
            而client 與 D之間的契約? 這個層面不"知新"是不行的。
            并且這個層面上的契約常常是出bug的地方 —— 因為這是語法檢查不了的, 必須有程序員自己去滿足語意。
             
            舉個例子 :
            看見一個虛函數(shù),它是否可以被覆蓋? 覆蓋它的實現(xiàn)是否需要同時調(diào)用基類實現(xiàn)?
            除非是約定俗成的一些情況, 比如 OnNotify、OnXXXEvent這種名字比較明顯。
            其他情況, 不去看文檔, 依然是不知道的。
             
             
            我剛剛就犯了一個錯。
            python 中 繼承HTMLParser , 覆蓋__init__ 時, 必須調(diào)用基類實現(xiàn)。
            我確實不知道構(gòu)造函數(shù)也可以被完全覆蓋……(原諒我…… 第1次使用python……)
            在C++中, 基類的構(gòu)造函數(shù)是無論如何都會被調(diào)用的。
             
             
            我沒有調(diào)用基類的__init__, 然后報了一個錯。
            好在報錯清晰, 源代碼的位置都有, 源代碼也可見, 源代碼也清晰。問題很容易就找出來了。
             
             
            如果
            4.1.
            它不是構(gòu)造函數(shù), 只是一個普通的虛函數(shù)?
            名字也看不出什么蹊蹺?
             
            4.2.
            文檔也沒有記載是否需要調(diào)用基類?
            (HTMLParser中的文檔沒有說要調(diào)用__init__, 這應(yīng)該是python的慣例, 所以就沒有記載了)
            沒有嚴(yán)格的錯誤檢查?
            沒有完整的、信息豐富的call stack trace?
            等發(fā)現(xiàn)錯誤時, 也許都離題萬里了。
             
            4.3.
            沒有清晰的源代碼(其實到了要查看源代碼的時候, 已經(jīng)是……)?
             

            這些問題在OO中同樣是存在的, 只是它沒引起編譯錯誤, 或者說編譯錯誤比較明顯, 容易修改。
            而更多的語意檢查, OO中 client 與 D之間的層次, 依然要靠程序員的學(xué)識 —— 主要是查閱文檔的習(xí)慣。

            對比GP
            4.1之前的4.0(OnNotify, OnXXXEvent之流), 同樣存在一些約定俗成。
             
            對4.1, 同樣是查文檔。
             
            對4.2, 沒有文檔同樣犯錯。
            C++中, 一部分錯誤可以提前到編譯時(C++只在持編譯時支持ducking type)
             
            對4.3
            同樣需要去查看源代碼。
             
            GP的代碼比OOP難看? 
            同上面, 只是因為不熟悉、不信任、不需要這種抽象方法, 這些契約的形式。
             

            STL中的契約形式其實并不多。
            [first,last) 左閉右開區(qū)間;
            iterator的幾個種類;
            (adaptable)binary(unary)_function;boost只是將其提升到N-nary,還省去了adaptable的概念;
            相等、等價、嚴(yán)格弱序。
            多嗎?
             
            而且, 我不覺得為了比較要去實現(xiàn)一個IComparable 有何美感可言……
             
             
            -------- -------- -------- -------- -------- -------- -------- --------
            5. 這些新的契約形式、 抽象方式, 有沒有其優(yōu)勢
            當(dāng)然有 —— 最小依賴帶來的靈活性
            上面也提到, GP只依賴于其需要的契約, 對其不需要的, 通通不關(guān)心。
             
             
            現(xiàn)在詳細解釋上面
            D d;
            f_oop(d); // D : I
             
            T v;
            f_gp(v);    // v.f
             
            為什么后者比前者靈活。因為后者只依賴所需要的東西。
             
            5.1
            template<typename T>
            void f123_gp(T v) { v.f1(); v.f2(); v.f3(); }
             
            可以使用
            struct T123_1 { void f1(); void f2(); void f3(); }
            struct T123_2 { void f1(); void f2(); void f3(); }
             

            void f123_oop(I& i) { i.f1(); i.f2(); i.f3(); }
            必須有一個
            struct I { virtual void f1(); virtual void f2(); virtual void f3(); };
            然后是:
            D1 : I; D2 : I; ...
             
             
            5.2
            如果現(xiàn)在需要實現(xiàn)另一個函數(shù), 它對T的需求更少 :

            template<typename T>
            void f12_gp(T v) { v.f1(); v.f2(); }

            T123_1, T123_2 可以使用
             
            新增一個:
            struct T12_1 { void f1(); void f2(); }
            依然可以使用
             

            OOP就必須重構(gòu)了:

            struct I12 { virtual void f1(); virtual void f2(); };
            struct I123 : I12 { virtual void f3(); }
            struct D12 : I12 {};
             
            5.3
            再看 :
            template<typename T>
            void f23_gp(T v) { v.f2(); v.f3(); }

            T123_1, T123_2 依然可以使用
            T12_1 不行。
             
            但新增的
            struct T23_1 { void f2(); void f3(); }; 可以使用
             
             
            OOP又必須重構(gòu):
            總體趨勢是這樣的, OOP需要極端靈活的時候, 就會變成這樣:
            一個接口, 一個函數(shù)
            struct I1 { virutal void f1(); };
            struct I2 { virutal void f2(); };
            struct I3 { virutal void f3(); };
            現(xiàn)在接口設(shè)計是極端靈活了。

            但使用接口時, 依然逃不過2種都不太優(yōu)雅的作法:
            1. 接口組合
            struct I12 : I1, I2;
            struct I23 : I2, I3;
            struct I31 : I3, I1;

            2. 接口查詢
            不組合出那些中間接口, 但運行時作接口查詢:
            void f12(I1* i1) {
              i1->f1();
              if (I2* i2 = dynamic_cast<I2*>(i1) {
                 i2->f2();
              }
              else { 將一部分編譯時錯誤留到了運行時。 }
            }
            這不是故意找茬, 而是將STL中的iterator換個簡單的形式來說明而已。
             

            也許絕大部分情況下, 是不需要靈活到每個接口一個函數(shù), 而是一個接口3、4個相關(guān)的函數(shù)。通常它們會被一起使用。

            即使沒有上面如此極端, 假設(shè)IPerfect1、IPerfect2都是設(shè)計得十分合理的, 3、4個函數(shù)的接口, 通常這3、4個函數(shù)要么必須一起提供, 要么都不提供, 單獨提供是不符合語意的, 提供太多又是不夠靈活的。
            這需要經(jīng)驗, 相當(dāng)多的經(jīng)驗。 但總是可以完成的事情。
             
            但組合接口, 依然是OOP的痛處。
            我記不清C#和Java中的interface是否繼承自多個interface
            如果不行, 它們就可能需要運行時接口查詢。
            而C++, 要在這種"組合接口"與接口查詢之前作一個選擇。
             
            反觀GP, 它一開始就不是以接口為單位來提供抽象,而是按需而定
            所以, 它既不需要仔細的拆分接口, 也不需要組合接口。
            STL中數(shù)據(jù)、容器、算法相互無關(guān)、可任意組合。
            應(yīng)該是前無古人的突破。
            后面有沒有來者? 上面已經(jīng)說了, OOP要達到這種靈活性, 同樣也有其代價。
            并且, OOP代價體現(xiàn)在丑陋, 而不是難以理解
             

            靈活的事物肯定比不那么靈活的事物難理解,抽象總比具體難理解。
            所以抽象出一個合理的、廣泛接受的語意很重要。

            * 就是解引用, ++ 就是前迭代, -- 就是后迭代。
            支持--就是雙向, 支持 + n 就是隨機。
             
            GP也不會胡亂發(fā)明一些語意不清晰的概念。
            window w;
            control c;
            w += c; 這種代碼在GP界同樣是收到批評的。
             
             
            最后, 軟件工程中, 是否真正需要靈活到如此程度, 以至于大部分人難以(或者不愿意去)理解的事物, 我就不知道了……
              回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 22:16 | 溪流
            @OwnWaterloo

            讀了這么多文字,真是受益良多。

            但必須指出的是,上面對于 OOP 和 GP 的某些比較是不公平的。在 OOP 中,只有你需要去繼承它的時候(即你需要修改這個類),你才需要了解源代碼,需要了解跟實現(xiàn)相關(guān)的“契約”。如果僅僅是使用的話,那么,給出全部 public class 的 public method 的聲明,應(yīng)該所有的使用者都不會犯語法錯誤,編譯器會提供完備的類型檢查。而同樣是使用(并不是去修改),GP 中,卻需要去了解它的實現(xiàn),這不是很不公平嗎?我們不是追求為了隱藏不必要的細節(jié),讓使用者減輕負擔(dān)嗎?當(dāng)然,如果要去修改它,那么對它的實現(xiàn)是必須要了解的了。
              回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 23:07 | OwnWaterloo
            @溪流
            嗯嗯, 你說得很對。

            假設(shè)多態(tài)的使用者與多態(tài)抽象之間的層面叫A, 還有多態(tài)提供者與多態(tài)抽象之間的層面叫B。
            OOP中的A和OB很相似, 只有B是新加的內(nèi)容。
            而GP中, 無論A、B, 都是新鮮內(nèi)容。

            所以這樣比較是不公平的~_~


            我至今沒有完全明白rbtree的實現(xiàn), 但不妨礙我使用map和set。
            對一般的內(nèi)建類型, 或者符合STL規(guī)范的值類型, 直接使用即可。
            如果需要放自定義類型,需要了解的就是key_compare和嚴(yán)格弱序。
            也不算多吧? OOP同樣離不開比較準(zhǔn)則, 只是key_compare換成了ICompare。
            如果還想高級一些, 還可以去了解allocator, 那是篇幅更長的一套規(guī)范。
            OOP沒見有這功能, 不比較了。

            boost中有一些庫, 我知道實現(xiàn)原理, 但不知道對付各種編譯器的trick。
            還有一些隱約知道原理, 更多的是不知道。
            boost的源代碼我一份都沒看過(堅持不下去…… 確實很丑陋, 但很多代碼都是為了對付不同編譯器而已)

            舉個例子, any。 即使沒看源代碼, 我也知道這個庫應(yīng)該在何時使用, 以及如何使用 —— 因為我看了文檔…… 不多的。 interface也需要看文檔的吧?

            但就是這么一個小東西, 使得我可以通過一個參數(shù)傳遞任何東西。
            并且不再需要繼承自某個基類……

            "一切皆為Object" —— 我覺得這是很可笑的事情。
            Finder.find?
            p1.fuck(p2); 還是 p2.fuck(p1);? 3p呢?
            太可笑了……

            而且在java和C#中實現(xiàn)free function其實并不難, 只是它們固執(zhí), 不愿意加入而已。

            OOP根本就不是一種純粹的編程范式。 不結(jié)合其他范式, OOP根本就寫不出程序來。 不知道那些追求所謂"純OO"的家伙是怎么想的 ……
            在我眼里, OO只有oo analysis, oo design, oo programming只是oo design而已。 實現(xiàn)design時, 是命令式的。
            至少對java, c#的OO來說是如此。

            不是我一人這么說, 你還可以去看看《冒號課堂》。


            上面有點小錯誤, 糾正一下:
            C/C++中本來就可以以單一參數(shù)傳遞任何東西……
            any 是使得這一作法類型安全而已。
            當(dāng)然, 既然使用C/C++寫代碼, 不同與那些保姆語言, 程序員自己就要對類型安全負責(zé)。 所以any通常是為了堵住那些對類型安全尤為重視的人的嘴而已。
            我還是更喜歡void* ...

            真不知道那些成天叫囂著類型安全, 卻視generic加入java是一種退步, 使得java不純粹的人是怎么想的……
            難道"不犯錯", 比"犯了錯總有人補救" 更重要?  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-30 00:04 | OwnWaterloo
            上上面關(guān)于interface和concepts的比較, 是因前不久給一個項目經(jīng)理解釋ducking type和以函數(shù)為單位的抽象形式, 但沒成功……

            現(xiàn)在算是想得比較清楚了, 但好像沒表達清楚…… 那么多文字, 我自己都不愿意看第2遍……


            整理一下:

            對單個"契約"的設(shè)計而言, 通過interface或是concepts沒有什么區(qū)別。
            并且interface比concepts更廣為人知。
            所以當(dāng)時怎么都沒能說服他……

            interface的劣勢(也就是concepts的優(yōu)勢)體現(xiàn)在"如何拆解與組合契約"上。
            如何拆分,設(shè)計interface, 是門學(xué)問。
            那幾個方法通常是應(yīng)該整體提供的?
            這就是interface的劣勢的本質(zhì) —— 它總是要將若干方法"打包"到一起才可以。
            極端情況, 只含有一個方法的interface也很常見, Dispose所屬接口就是。

            interface更麻煩的地方在于組合。
            當(dāng)一個f需要若干種interface的功能時, 要么必須將一些interface繼續(xù)"打包", 形成一個新的, 要么運行時檢查。


            而concepts打從一開始, 就僅僅依賴所需要的東西, 不需要"打包"這一過程。
            拆分和組合是自由的。
            從某種程度上來說, 這也容易造成晦澀的設(shè)計。
            但什么設(shè)計不是學(xué)問呢…… interface的設(shè)計同樣需要經(jīng)驗。


            concepts相比interface來說, 限制更少, 也更靈活。
            這種靈活性是本質(zhì)上的 —— 因為不存在"打包"這一限制 ——是不以現(xiàn)實編程中是否需要這種程度的靈活性,以及這種靈活性是否會被人們普遍接受而轉(zhuǎn)移的。


            靈活的事物通常難以掌握, 如果現(xiàn)實編程中不需要, 人們不理解、不接受, 也只能算是當(dāng)前條件下的劣勢 —— 對太多數(shù)人來說晦澀、復(fù)雜, 無法被廣泛使用。
            如果哪天人們接受了呢?



            還有一點, 上面說的GP和OO、OOP概念太廣泛。 OO、OOP的定義到底是什么, 各有各的說法。
            所以改為 concepts 和 interface( in java and C# )的比較, 比較準(zhǔn)確。
            并且某些其他支持OO的語言 —— 現(xiàn)在是個語言都要說自己支持OO, 不支持的也要改為支持... ——中, 是不需要interface這種"契約打包器"的。



            樓主啊, 我自我感覺比較好的建議, 就只有建議你實現(xiàn)一套侵入式容器(包括樹式堆)而已, 既鍛煉了技巧, 又不陷入"重復(fù)發(fā)明輪子"。

            其他的, 都是自說自話 …… 不必想太多-_-

            因為做上這行了,沒什么時間總結(jié)平時的一些零散想法。
            如果是中學(xué)做習(xí)題, 會因為做著做著, 就累了 …… 就轉(zhuǎn)而"總結(jié)一下吧,比單純做來得有效, 同時休息休息"。
            而寫代碼…… 是沒有累的感覺的…… 就會一直寫下去……
            只有在討論的時候, 才會想去總結(jié)一些東西。
            把你的blog評論區(qū)當(dāng)吐槽了…… sorry ...
              回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-30 00:49 | 溪流
            @OwnWaterloo

            非常歡迎吐槽~哈哈
            關(guān)于侵入式和非侵入式,說實話我也是前幾天第一次聽你說。我稍微查了下,不是很確切的了解其含義。前面我在 Iterator 里放了個 Array *m_pArray,后來拿掉了,改成放一個 ValueType *,有沒有改變侵入式/非侵入式方面的屬性呢?  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-10-01 14:16 | OwnWaterloo
            @溪流
            我out了……

            boost 已經(jīng)有 intrusive 容器的實現(xiàn)了……
            http://www.boost.org/doc/libs/1_35_0/doc/html/intrusive/intrusive_vs_nontrusive.html

            我這里的boost版本非常老…… 1.33…… 所以沒發(fā)現(xiàn)……  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-10-12 18:39 | 陳梓瀚(vczh)
            @OwnWaterloo
            當(dāng)然interface比起純GP還有一個好處,就是你可以對一個具體的類,譬如IEnumerable<int>得到類型推導(dǎo)結(jié)果int,但是你沒法對一個隨便什么object只是長得跟IEnumerator<int>一樣的東西來得到類型推導(dǎo)結(jié)果int。這(類型推導(dǎo))是GP更大的價值,可以跟OOP一起,OOP充當(dāng)抽象,GP用來做推導(dǎo),能做出很多漂亮的用法來。譬如說:

            MyList<int> list;(繼承自IEnumerator<int>)
            list>>Where(某個函數(shù))的結(jié)果一定是IEnumerator<int>,編譯器推導(dǎo)了。

            反而vector<int>也想要支持的話,你會發(fā)現(xiàn)所有的容器都要寫一遍……所以我只需要讓我的容器都支持IEnumerator<T>,然后GP對IEnumerator<T>進行類型推導(dǎo)即可,提供10個功能我只寫10個函數(shù)。如果是vector<int>+list<int>+queue<int>什么的,提供10個功能要寫10×n個函數(shù),n==容器數(shù)量。

            當(dāng)然這僅僅是對于【容器】而言的。至于你剛才討論的很多都是其他的東西,就不納入了。  回復(fù)  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-10-12 19:15 | OwnWaterloo
            @陳梓瀚(vczh)
            需求是iterator類型推導(dǎo)iterator解引用后的類型?
            嘿嘿,真的不能嗎?

            #include <iterator>
            MyIt it1,it2;
            typename std::iterator_traits<MyIt>::value_type // 這就是你要的
            v = *it1;

            // 除此之外還有其他幾種類型信息。
            typename std::iterator_traits<MyIt>::pointer p = &v;
            typename std::iterator_traits<MyIt>::reference r = v;
            typename std::iterator_traits<MyIt>::difference_type d
            = std::distance(it1,it2);

            typename std::iterator_traits<MyIt>::iterator_category tag;
            tag決定了iterator能做什么。

            algorithm中的所有需要iterator的算法,都和容器完全無關(guān)。
            和iterator相關(guān)的類型信息都是這么取得的。
            自然不存在M*N(M=算法的數(shù)量,N=容器的數(shù)量)的說法。
            而是M(M=算法數(shù)量)。


            給你舉個例子可能比較明顯:
            需要value_type的算法我一時還想不出來……
            來個其他的需要difference_type的意思意思:
            template<typename InIt,typename T>
            typename iterator_traits<InIt>:: difference_type // 返回類型
            count(InIt first,InIt last,const T& v) {
            typename iterator_traits<InIt>:: difference_type c = 0;
            for (;first!=last; ++first) if (*first==v) ++c;
            return c;
            }



            這是GP的類型推導(dǎo)方式 —— 嵌入類型。這用法漂亮嗎?


            實現(xiàn)這一魔法的工作:
            iterator_traits只需實現(xiàn)一次。
            各個容器其實不需要做什么工作。
            工作在每一個iterator類型上,它必須有這幾種嵌套類型。
            當(dāng)然,iterator頭文件中提供了std::iterator, 可以繼承自它而得到這幾種嵌套類型。
            不過我一般喜歡自己寫,繼承可能會影響空基類優(yōu)化。
            多嗎?


            而且,有一點OOP是辦不到的。 所有的iterator都不需要有一個"基類"。
            T arr[size];
            &arr[0] 類型是T*, 是iterator,而且還是random_access。但是它的基類應(yīng)該是什么才好呢?
            它只有4字節(jié)。OOP就那個vptr就4字節(jié)了,加點數(shù)據(jù)再加上padding,8字節(jié)至少。



            "反而vector<int>也想要支持的話,你會發(fā)現(xiàn)所有的容器都要寫一遍……"
            這是什么需求? 需要所有容器都寫一遍? 說出來看看?  回復(fù)  更多評論
              
            久久精品国产亚洲av麻豆蜜芽| 亚洲成色www久久网站夜月| www亚洲欲色成人久久精品| 久久强奷乱码老熟女| 影音先锋女人AV鲁色资源网久久 | 欧美一级久久久久久久大片| 亚洲国产成人久久精品99| 91精品国产综合久久久久久| 性做久久久久久久久久久| 嫩草影院久久国产精品| 99久久无色码中文字幕人妻| 久久久久亚洲?V成人无码| 精品久久久久久无码专区不卡| 亚洲伊人久久综合影院| 亚洲综合久久综合激情久久| 亚洲乱码精品久久久久..| 日韩久久无码免费毛片软件| 亚洲国产精品婷婷久久| 久久av无码专区亚洲av桃花岛| 亚洲国产成人精品久久久国产成人一区二区三区综 | 99久久免费国产精品热| 亚洲精品乱码久久久久久| 久久影视综合亚洲| 日韩一区二区久久久久久 | 久久综合九色欧美综合狠狠| 久久er国产精品免费观看2| 亚洲国产精品无码久久一区二区| 开心久久婷婷综合中文字幕| 99热精品久久只有精品| 91精品国产高清91久久久久久| 亚洲av成人无码久久精品| 久久精品国产亚洲αv忘忧草 | 国产精品久久久久久久人人看 | 国产精品美女久久久m| 人妻精品久久久久中文字幕69| 国产偷久久久精品专区| 2020久久精品亚洲热综合一本| 久久国产视屏| 日本亚洲色大成网站WWW久久| 久久久久国产精品嫩草影院 | 天堂无码久久综合东京热|