• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 體驗Visual C++.NET 2005中的STL

    發表于:2007-05-25來源:作者:點擊數: 標簽:C++.NET體驗visual2005中的
    為了更好的使STL適合.NET 開發 ,Visual C++產品組,在2005版的Visual C++中重新設計了STL,并命名為STL.NET,從Beta1版本的產品中開始提供。 在STL.NET的設計中,STL的實現使用了CLI泛型和C++模版機制。2005版本的C++將加入C++/CLI動態編程的支持,應當會成
    為了更好的使STL適合.NET開發,Visual C++產品組,在2005版的Visual C++中重新設計了STL,并命名為STL.NET,從Beta1版本的產品中開始提供。

      在STL.NET的設計中,STL的實現使用了CLI泛型和C++模版機制。2005版本的C++將加入C++/CLI動態編程的支持,應當會成為最能夠滿足程序員設計的語言。

      給予程序員豐富的選擇

      總共有三個容器庫可供程序員用于操作CLI類型,這三個容器庫建于三種類型參數化模型之上。

      原先元素類型存儲的Systems::Collection 庫是基于CLI類型中的對象基類來實現的。如下的 ArrayList實現了IList接口。它代表類型對象的數組,在本例中用于控制String類型的元素。(這里采用版本2的語法來實現)

    void objectCollection()
    {
     using namespace System::Collections;
     ArrayList ^as = gcnew ArrayList;
     as->Add( "Pooh" ); as->Add( "Piglet" );
     as->Add( "Eeyore" ); as->Add( "Rabbit" );
     as->Sort();
     Console::WriteLine( "ArrayList holds {0} elements: ",as->Count );
     for ( int i = 0; i < as->Count; i++ )
      Console::WriteLine( as[ i ] );
      int index = as->IndexOf( "Pooh" );
      if ( index != -1 )
      {
       //需要一個清晰地downcast
       String^ item = safe_cast( as[ index ]);
       as->RemoveAt( index );
      }
      as->Remove( "Rabbit" );
      Console::WriteLine( "\nArrayList holds {0} elements: ",as->Count );
      IEnumerator^ is = as->GetEnumerator();
      while ( is->MoveNext() )
       Console::WriteLine( is->Current );
    }

      現在我們引入了一個基于CLI泛型機制的新的容器庫??梢栽赟ystem::Collections::Generic 命名空間中找到。這是在Visual Studio 2005 Beta1中的實現,在最終的發布版當中可能會有所改變。Collection 是一個具體的泛型基類,用戶們可以從其中派生自己特化的容器類。下面的樣例與上面的例子作用相同,只是使用了新的容器庫,



      STL.NET提供了一個與以往設計風格迥異的類型參數化模型,我們將在下個話題中談到 。下面是String容器的實現。

    #include
    #include

    void stlCollection()
    {
     vector ^svec = gcnew vector;
     svec->push_back("Pooh"); svec->push_back("Piglet");
     svec->push_back("Eeyore"); svec->push_back("Rabbit");
     //泛型算法:sort
     sort( svec->begin(), svec->end() );
     Console::WriteLine( "Collection holds {0} elements: ",svec->size() );
     for ( int i = 0; i < svec->size(); i++ )
      Console::WriteLine( svec[ i ] );
      //泛型算法:find
      vector::iterator iter = find( svec->begin(), svec->end(), "Pooh" );
      if ( iter != svec->end() )
      {
       //不需要downcast……
       String ^item = *iter;
       svec->erase( iter );
      }
      //泛型算法: remove……
      remove( svec->begin(), svec->end(), "Rabbit" );
      Console::WriteLine( "\nCollection holds {0} elements:",svec->size() );
      IEnumerator ^is = svec->GetEnumerator();
      while ( is->MoveNext() )
      Console::WriteLine( is->Current );
     }



      為什么要選用STL.NET?

      在我們深入STL.NET之前,讓我們首先來簡要地回答一個不可避免的問題:Visual C++程序員為什么要選用STL.NET容器類而不是語言中立的系統:Collections或者System::Collections::Generic 庫?

      立即放棄System::Collections庫和Visual Studio 2005決定提供泛型褲的原因是一樣的:由于類型信息的丟失,經常會造成參數化對象模型非常的復雜并且不安全。在簡單的使用中,例如在容器中裝有16個或者更少的元素,進行冒泡排序的時候還可以使用。但當你的應用程序涉及到真實世界的問題的時候,你就必須要提供一個更為完善的解決方案了。

      所以,STL.NET和System::Collections::Generic庫便成為如Visual C++這樣的系統級程序設計語言的備選方案。為什么Visual C++程序員應當偏愛于STL.NET呢?這不就使我們的程序與其他的.NET語言隔離開了么?這是一個很現實的問題,并且也值得作出一個答復。

      回復之一是“可擴展性(extensibility)”。最初STL的設計模式是由Alex Stepanov發明的, 他將算法和容器存放在了不同的域空間中。這樣用戶就可以將所有容器都適用的算法添加到算法集當中去,或者將每個算法都可以應用的容器添加到容器集當中去。泛型庫是一個更為傳統的模型。這就引出了我們第二個回復。

      第二個回復是“統一性(unification)”?,F在正在使用C++的程序員使用這個庫和現有的代碼的水平已經達到了專家水準。我們不僅僅希望能夠提供一個對現有代碼遷移的途徑,同時也希望使程序員們積累下來的專家經驗仍然適用。如果在你原來進行C++編程的時候依賴于STL,并且很難想象一個C++程序員不依賴于STL,那么它在.NET中的消失會使你感到是一個很大的失誤—至少這是我曾經的體會。與我曾經交流過的很多C++專家都曾經提到過這個問題,并且因此表示對遷移到.NET持保留態度。

      第三個回復是“性能”。但是C++程序員對于討論性能這個話題早已顯得不愿意再提這些陳詞濫調,我這里只是一筆帶過—在后續的文章中將深入討論。

      最后問題是。這些做的都非常棒并且非常好Stan, 但是這不是將C++程序員和C++/CLI程序與.NET社群的其它部分隔閡開了么?對于這個問題的回答,我認為是一個非常簡單的“不”。STL.NET的體系架構師,包括Anson Tsao, Martyn Lovell, 和P.J. Plauger, 已經非常慎重地考慮了這個問題,通過對IEnumerator, IList, 和ICollection的支持,我們對于STL.NET能夠和其它.NET 語言共同操作的能力非常有信心。我們將在后續的系列文章中深入的討論。

      定義一個公共基礎

      深入了解并使用STL.NET的方法有兩種:一種途徑是可以通過了解STL和STL.NET的區別來掌握。另外一種途徑是通過了解他們有哪些共性。雖然那些有STL豐富使用經驗的人來說似乎需要一張“區別清單”,然而這并不能使你從那些不熟悉的庫的烏煙瘴氣中逃離出來,所以說,當我們放慢腳步,深入這些或那些容器以及他們是如何與系統集合庫(System collection libraries)互操作等這樣的深奧角落的時候—雖然這些都是非常整潔優雅的,但我們直接的去了解這些新的內容不是更好么?至少在下面的簡短介紹部分我希望大家是這樣做的。通過這種方法,對于那些新手來說就可以對STL和STL.NET提供的參數化集合的擴展模型有一個了解。

      那么STL和STL.NET都有哪些共性呢?這兩者都包括兩個主要的組件:順序式(Sequential)和關聯式(associative)容器類型,以及一個泛型算法集。(是的,如果你是一個經驗豐富的STL開發者,你就應當知道我下面要討論什么。同樣,兩者具有同樣的術語和背景,所以你得耐心點。) 泛型算法不直接操作容器類型。取而代之的是,我們使用了一個迭代器來成對的標識用于操作的元素范圍。元素范圍符號(正式的術語為左包含區間)如下所示:

    // 讀作:包含first直至但是不包括last

    [ first, last )

      這表明了范圍是從first開始以last結束但是不包含last。當first等于last的時候這個范圍就是空了。

      順序式容器中存儲了單獨類型元素的序列集。vector和list類型是兩個主要的順序式容器。(第三個順序式容器是deque—發音deck—提供了vector同樣的功能,但是用于對高效插入和刪除第一個元素這種特殊情況。一般我們首選deque而不是vector,例如一個隊列的實現。)

      我們在訪問一個連續容器類型之前,我們必須包含進合適的頭文件,如下所示:

    #include

    #include

    #include

      這些頭文件同時也包含了共享基類接口的聲明,例如interface_vector,以及泛型在容器中的應用,例如generic_vector。



      關聯式容器(associative container)支持對現有元素的顯示和檢索的高效查詢。關聯式容器類型中最主要的兩個就是map和set。Map是一個key(鍵)/value(值)對:key用于查詢,value用于存儲和檢索數據。舉個例子來說,電話號碼簿可以用map簡單地表示出來:key是每一個人的名字,value是相關聯的電話號碼。

      map使用帶下劃線的樹抽象來按照值升序排列條目。hash_map容器能夠進行更為高效的檢索排序。盡管如此,hash_map迭代也是某種程度上隨機地訪問元素。如果檢索是map中的主要活動,就應首選hash_map容器。

      set包含一個單鍵值(key value) 并且支持元素是否存在的高效查詢。例如,文本查詢系統在建立文本詞庫的時候可能需要建立一個常用詞語集并將之排除在文本外,例如the, and, but, 等等。程序將輪流的讀取文本中的每一個詞,檢查它是否是拒絕接納詞匯集中的詞匯,根據查詢的結果,或者是忽略這個詞,或者是將之存入數據庫。除了set以外,還有hash_set 容器,它與map和hash_map有相同的特性。

      map和set只能包含一個鍵(key)。multimap和multiset 支持出現多個同樣的鍵。還以我們的電話號碼簿為例,我們可能需要將一個人列在多張表中。在這種情況下,我們就要使用multimap。同樣也存在hash_multimap和hash_multiset。

      與這些容器類型相關聯的頭文件如下所示:

    //用于map和multimap
    #include
    //用于set和multiset
    #include
    //用于hash_map和hash_multimap
    #include
    //用于hash_set和hash_multiset
    #include


      一個簡單的演示

      讓我們通過一個實例來具體地討論一下。下面的例子實現了文本詞匯統計。它展示了map, hash_set, 和vector的使用。函數聲明如下:

    map^

    build_word_count( vector^>^ words,

    array ^common_words )

      模板語法讓人看起來非常地復雜。讓我們看看能夠做哪些有益的改進。build_word_count() 的返回類型是map,鍵(key)是字符串—文本中每一個不同于其他詞的單詞—值(value)是用于統計相關聯詞出現次數的整數。函數的第一個參數是CLI數組向量(vector),它將文件中的詞匯保存為字符串元素。第2個參數中保存著我們要排除在外的字符集。

      由于很多“帽子”和右中括號間隔出現,這看起來相當的復雜。我們可以使用typedef從文字上化簡一下:

    typedef array^ words;
    typedef vector^ text;

      使用typedef重定義的函數如下所示:

    map^
    build_word_count( text theText, words common )

      我們也可以除去顯式的map聲明,但是出于讀者練習的考慮我仍然保留了聲明。下一個工作是實現。我們可以將之分為兩部分:(1) map和hash_set的初始化,如下所示,(2)實際的文本處理程序。



      數組元素中的第一個直至有效的最后一個元素的地址裝入hash_set的構造函數為元素裝入hash_set提供了同等的機會。這兩個地址為hash_set 構造函數的迭代提供了一個元素范圍,輪流的將它們插入容器中。這就是我在這部分開始提到的迭代器的范圍。

      實現的第2部分便是文本處理。因為我們還沒有看到官方正式發布的迭代器,我們現在仍然使用for循環來遍歷vector。我們將使用for each語句來遍歷vector的每一個數組元素。這就是如上所述的代碼:

    //循環的訪問每一個數組

    for ( int ix = 0; ix < theText->size(); ++ix )
    {
     for each ( String^ wd in theText[ ix ] )
      if ( common->find( wd ) == common->end() )
       word_map[ wd ]++;
    }

    // 不要忘記返回結果……
    return word_map;

      find() 是hash_set的成員函數,它用于確定詞匯項是否已經存在于set當中—所有的關聯式容器都支持find()成員函數。(是的,順序式容器,以及內置數組,和所有你可能將集成到STL/STL.NET模型中的集合,都使find()泛型算法。算法和容器的隔離理論上比實際上要為嚴重。我們將特別地討論一下列表順序式容器(list sequential container) 。)他返回的是我還沒有討論過的—容器中的迭代器。此時,我們將迭代器假想為指針類型。如果在容器中詞匯項已經存在了,那么find()將返回給它一個迭代器;否則,將返回最后一個詞匯項的迭代器。這與end()返回的迭代器值是相同的。我們判斷STL下的元素搜索是否成功的方法就是比較用搜索方法返回的迭代器值與end()方法返回的迭代器值是否相同。如果兩者相同,那么元素便不在容器中。

      每一個容器都提供begin()和end()成員函數。begin()返回給容器中first元素一個迭代器。正如我前面說到的,end()返回給容器中last元素一個迭代器。舉例如下,下面的程序中展示了我們如何聲明兩個迭代器并且用這兩個成員函數初始化它們,

    vector::iterator first = theText->begin();
    vector::iterator last = theText->end();

      容器的遍歷一般使first在一個for循環或者是while循環中遞增,當first等于last的時候終止循環。例如,

    for ( ; first != last; ++first )

      對迭代器對的要求是他們必須能夠通過增量運算符的重復應用以first開頭并能夠達到last。盡管如此,編譯器自身不能做到;如果不能夠達到這個要求則會產生未定義的運行時行為(undefined run-time behavior)。

      我們通過反引用(*)操作符訪問迭代器涉及到的元素。例如,為了重新獲得我們的向量(vector)的每一個CLI數組元素,我們在上述的for循環體中寫入下面的代碼,

    array ^as = *first;

      我們將在這一系列文章的后續文章中介紹關聯式容器的聲明和用法。


      算法的選擇

      泛型算法為這些容器類型和兩個內置數組類型的元素提供了操作。這些操作包括Search (find, count), Sort (merge, partition, permutate, reverse, rotate, shuffle, sort), Deletion/Subtitution (remove, replace, swap, unique), Copy, Relational (equal, min, max, includes), Generate (fill, for-each, generate, transform), Set (union, intersection, difference), Heap (make, sort, pop, push), 以及很多深奧的數字操作,例如積分,部分求和,內積(inner product),和臨差(adjacent difference)。

      在我們使用泛型算法前,當然,我們必須包含進相應的頭文件。除了數字算法之外的其它所有算法,我們都這樣寫:

    #include

      四個數字算法,積分,部分求和,內積,和臨差被分出一個單獨的頭文件。在使用這些算法的時候我們這樣寫頭文件:

    #include

      (我一直在尋求為什么這四個操作的頭文件被單獨的提出來的原因—這顯然將算法的使用的討論復雜化—但是無論是在文檔中還是經過深入的研究,我仍然找不到答案。在原先Stepanov的實現中,這四個操作與其它的算法使用的是同一個頭文件。在標準化過程中, numeric頭文件被引入了并且在數字庫標準選舉部分確實做了一個討論。)

      你可能會想,天哪,Stan,這些不應當寫作嗎?畢竟,這是容器頭文件的驗證方法。如果那樣寫的話不是有點弄巧成拙么?令人吃驚的是,答案是“不”。我說令人吃驚的是因為STL和STL.NET中采用了相同的實現方法。天哪,我們討論到了代碼重用去了!

      好的。讓我們看看如何使用這些算法。舉例來說,讓我們在將字符串元素數組插入map之前對它們進行排序。我們將使用sort()泛型算法來實現它。對于所有的泛型算法,參數幾乎都是迭代器對構成的范圍:

    sort( &as[0], &as[ as->Length ] );

      在開始的代碼中還要使用到另外兩個泛型算法—find()和remove():

    //泛型算法:find

    vector::iterator iter = find( svec->begin(), svec->end(), "Pooh" );

    if ( iter != svec->end() ){ ... }

    //泛型算法:remove……

    remove( svec->begin(), svec->end(), "Rabbit" );

      到現在為止我們應當對這里的用法模式非常敏感了:由迭代器對劃分出的范圍與容器的算法密不可分。搜索算法返回一個迭代器,或者用于發現容器中的項,抑或是找不到,迭代器用于標識范圍內的最后一個項。

      我們將用泛型算法做一些有趣的事情—但這必須在后續的文章中完成。我希望這些內容給我們了一個我們將要討論什么的不錯的概覽,并且為我們能夠作為一個學習小組向前走提供了一個公共的詞匯表和背景知識。

    原文轉自:http://www.kjueaiud.com

    評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>