• <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;

                };
            }

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

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

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

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

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

            如果有個 template <T> where T is IIterator,用戶看接口定義就知道該怎么做了;可是現在,只能到調用 Iterator 的某個具體方法的時候才報錯,用戶理解這個錯誤,就要理解我的實現了。這不僅增加了學習代價,還在一定程度上違背了封裝的初衷,不是么?一點變通的辦法都沒有嗎?
              回復  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 15:55 | 陳梓瀚(vczh)
            如果你對效率要求不是跟人家開發SqlServer的后臺一樣高的話,那就來一個虛基類吧,當成C#的interface用用。虛基類的作用主要在于你可以用他搞類型計算,譬如說來一個iterator你就可以知道它的element type是什么,而不用跟stl里面的一樣要求你必須在iterator的里面typedef。而且你還可以做很多iterator<A>到iterator<B>的轉換,從map<a,b>到iterator<pair<a,b>>的轉換等等。就是慢一點點罷了(真的是一點點)。  回復  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 15:56 | 陳梓瀚(vczh)
            @OwnWaterloo
            壞消息是,那些編譯錯誤根本看不懂。  回復  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 15:56 | 陳梓瀚(vczh)
            @陳梓瀚(vczh)
            我寫了一個錯誤的iterator然后錯誤消息竟然是list<T>里面的哪個代碼用了一個不存在的函數  回復  更多評論
              
            # 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)
            {
            ....
            }  回復  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 16:14 | 陳梓瀚(vczh)
            @溪流
            而且我認為作為一個iterator,我絕對不會想到是Array.SetValue來通過它修改自己的值的  回復  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-09-29 18:33 | 溪流
            @陳梓瀚(vczh)

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

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

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

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

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

            OO編程:

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

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

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

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

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

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

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

            GP:
            GP總共有哪些契約形式, 我總結不出來。
            但至少有一條 —— 它不再對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、 其他范式都有。
            它是合作與復用的前提。
            -------- -------- -------- -------- -------- -------- -------- --------
             
             
            3.
            為什么GP飽受爭議, 而OO沒有?
            我覺得, 是因為從OB到OO過度比較自然
             
             
            要求一個I需要怎樣, 一個I需要使用者怎樣的時候,
            通常也會有類似的需求 —— 要求一個C怎樣, 一個C需要使用者怎樣。
            注意, 這只是 client 與 I, 以及 client 與 C之間的契約。
            在這個層面上, 不需要學習新的思想。
            而下面會提到OO引入的新的契約形式。
             

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

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

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

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

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

            STL中的契約形式其實并不多。
            [first,last) 左閉右開區間;
            iterator的幾個種類;
            (adaptable)binary(unary)_function;boost只是將其提升到N-nary,還省去了adaptable的概念;
            相等、等價、嚴格弱序。
            多嗎?
             
            而且, 我不覺得為了比較要去實現一個IComparable 有何美感可言……
             
             
            -------- -------- -------- -------- -------- -------- -------- --------
            5. 這些新的契約形式、 抽象方式, 有沒有其優勢
            當然有 —— 最小依賴帶來的靈活性
            上面也提到, GP只依賴于其需要的契約, 對其不需要的, 通通不關心。
             
             
            現在詳細解釋上面
            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
            如果現在需要實現另一個函數, 它對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就必須重構了:

            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又必須重構:
            總體趨勢是這樣的, OOP需要極端靈活的時候, 就會變成這樣:
            一個接口, 一個函數
            struct I1 { virutal void f1(); };
            struct I2 { virutal void f2(); };
            struct I3 { virutal void f3(); };
            現在接口設計是極端靈活了。

            但使用接口時, 依然逃不過2種都不太優雅的作法:
            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換個簡單的形式來說明而已。
             

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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


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

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

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


            整理一下:

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

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

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


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


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


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



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



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

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

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

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

            boost 已經有 intrusive 容器的實現了……
            http://www.boost.org/doc/libs/1_35_0/doc/html/intrusive/intrusive_vs_nontrusive.html

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

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

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

            當然這僅僅是對于【容器】而言的。至于你剛才討論的很多都是其他的東西,就不納入了。  回復  更多評論
              
            # re: 算了,還是不 typedef 了,類型真煩 2009-10-12 19:15 | OwnWaterloo
            @陳梓瀚(vczh)
            需求是iterator類型推導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的算法,都和容器完全無關。
            和iterator相關的類型信息都是這么取得的。
            自然不存在M*N(M=算法的數量,N=容器的數量)的說法。
            而是M(M=算法數量)。


            給你舉個例子可能比較明顯:
            需要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的類型推導方式 —— 嵌入類型。這用法漂亮嗎?


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


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



            "反而vector<int>也想要支持的話,你會發現所有的容器都要寫一遍……"
            這是什么需求? 需要所有容器都寫一遍? 說出來看看?  回復  更多評論
              
            国产成人精品久久一区二区三区av| 国产精品久久国产精麻豆99网站| 国产亚洲成人久久| 欧美成a人片免费看久久| 欧美日韩久久中文字幕| 久久综合香蕉国产蜜臀AV| 99久久综合国产精品二区| 久久久无码精品亚洲日韩蜜臀浪潮 | 中文国产成人精品久久亚洲精品AⅤ无码精品 | 亚洲精品午夜国产va久久| 欧美黑人又粗又大久久久| 精品无码久久久久久国产| 亚洲国产另类久久久精品小说| 91麻精品国产91久久久久| 精品久久久无码人妻中文字幕| 伊人久久大香线蕉影院95| 亚洲综合日韩久久成人AV| 国产成人综合久久久久久| 国产午夜精品久久久久免费视| 美女久久久久久| 久久高清一级毛片| 91性高湖久久久久| www性久久久com| 69SEX久久精品国产麻豆| 国产成人精品三上悠亚久久| 久久精品成人免费观看97| 久久这里只有精品首页| 国产A三级久久精品| 伊人久久大香线蕉综合热线| 久久免费视频6| 岛国搬运www久久| 精品久久久久久中文字幕| 精品久久久久久无码专区| 亚洲国产精品无码久久久蜜芽| 模特私拍国产精品久久| 亚洲а∨天堂久久精品| 久久夜色精品国产亚洲av| 久久精品国产只有精品66| 久久免费视频6| 伊人热热久久原色播放www| 精品国产日韩久久亚洲|