程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#與C++相比較之STL篇

C#與C++相比較之STL篇

編輯:C#入門知識

引言   Program into Your Language, Not in It——《代碼大全》。如何深入一門語言去編程?我認為有三步:熟悉它;知道它的局限性;擴展它。如何熟悉?不必說,自然是看書看資料,多用多寫。如何知曉其局限性?這步我們只能通過對比了,任何事物都有其自身的局限性,沒有任何東西是完美的(除了上帝哈Smile)。在這裡,我用C#與C++做對比,嘗試勾勒出C#與C++一些觀念上的不同。如何擴展?這點我正在嘗試Embarrassed smile。   C++的STL   STL包含六大組件:容器(Containers)、迭代器(Iterators)、算法(Algorithms)、仿函數(functors)、配接器(Adapters)、配置器(Allocators)。容器通過配置器取得數據存儲空間,算法通過迭代器來存取容器的內容,仿函數協助算法完成不同的操作策略,配接器用來修飾或套接仿函數。這一整套配合,可以使我們完全掌控數據在存儲器上的增刪查改。(在這裡我很想畫一張圖出來,但是我找了很久,實在找不到好的工具,有沒有哪位同學能分享一些好的畫示意圖之類的工具呢?)   容器   STL中,最常用的容器要算vector、list、map、set這四種了。C#中,對應的容器分別是:List、LinkedList、Dictionary、HashSet。單看容器,其實它只是抽象出了一些邏輯結構,根據不同的邏輯需要,在存儲器上反應出不同的物理存儲結構。這點C++和C#的抽象沒有什麼不同,當然,其實現上,很不相同。這點通過代碼的書寫,就可以略窺一斑。   C++代碼如下:   #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <algorithm> using namespace std;       int main() {     vector<int> vec;     list<int> lst;     map<int,int> mp;     set<int> st;       for (int i = 0; i < 10; ++i)     {         vec.push_back(i);         lst.push_back(i);         mp.insert(make_pair(i, i));         st.insert(i);     }       cout << "vector: " << endl;     vector<int>::const_iterator iterVec(vec.begin());     while (iterVec != vec.end())     {         cout << *iterVec << endl;         ++iterVec;     }       cout << "\nlist: " << endl;     list<int>::const_iterator iterLst(lst.begin());     while (iterLst != lst.end())     {         cout << *iterLst << endl;         ++iterLst;     }       cout << "\nmap: " << endl;     map<int, int>::const_iterator iterMap(mp.begin());     while (iterMap != mp.end())     {         cout << "Key = " << iterMap->first              << "Value = " << iterMap->second              << endl;         ++iterMap;     }       cout << "\nset: " << endl;     set<int>::const_iterator iterSet(st.begin());     while (iterSet != st.end())     {         cout << *iterSet << endl;         ++iterSet;     }     }   C#代碼如下:   using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections; using System.Net.Sockets; using System.Net;   namespace ConsoleApplication1 {     class Program     {         static void Main(string[] args)         {             List<String> list = new List<String>();             LinkedList<String> linkedList = new LinkedList<String>();             Dictionary<Int32, String> dic = new Dictionary<Int32, String>();             HashSet<String>  set = new HashSet<String>();               for (int i = 0; i < 10; ++i )             {                 list.Add(i.ToString());                 linkedList.AddLast(i.ToString());                 dic.Add(i, i.ToString());                 set.Add(i.ToString());             }               Console.WriteLine("List: ");             foreach (var item in list)             {                 Console.WriteLine(item);             }               Console.WriteLine("\nLinkedList: ");             foreach (var item in linkedList)             {                 Console.WriteLine(item);             }               Console.WriteLine("\nDictionary: ");             foreach (var item in dic)             {                 Console.WriteLine("Key = {0}, Value = {1}", item.Key, item.Value);             }               Console.WriteLine("\nHashSet: ");             foreach (var item in set)             {                 Console.WriteLine(item);             }         }     } }   C++並沒有內置的foreach語句(貌似新的標准中有?),所以它通過迭代器來幫助它來完成迭代。而C#就非常方便了,在語法級別完成了這個功能。從寫法上我們可以看到,c++的迭代器看上去是一個指針,是一個可以做自增操作的指針。c#迭代出的每個item則是當前存放的數據。   迭代器   STL中的迭代器有五種:輸入迭代器(Input Iterator)、輸出迭代器(Output Iterator)、前向迭代器(Forward Iterator)、雙向迭代器(Bidirectional Iterator)、隨機存取迭代器(Random Access Iterator)。C#中,沒有相對應的迭代器概念。畢竟迭代器就是一個智能指針,而C#卻不支持指針(unsafe另算哈)。   輸入迭代器,只能一次一個向前讀取元素,並且只能讀取該元素一次。如果我們復制一份輸入迭代器,副本輸入迭代器和原來的輸入迭代器分別向前讀取一個元素,那麼他們可能會遍歷到不同的值。以istream_iterator為列,代碼如下:   #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <algorithm> #include <iterator> #include <string> using namespace std;       int main() {     //按Ctrl+Z結束輸入,或者按Ctrl+C取消輸入     istream_iterator<string> iterBegin(cin);     istream_iterator<string> iterEnd;     while (iterBegin != iterEnd)     {         cout << *iterBegin << endl;         ++iterBegin;     } }   輸出迭代器,與輸入迭代器相反,它的作用是將元素值一個個寫入,所以只能作為左值。以ostream_iterator為列,代碼如下:   #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <algorithm> #include <iterator> #include <string> using namespace std;       int main() {     ostream_iterator<int> iter(cout, "\n");     vector<int> vec ;     for (int i = 0; i < 10; ++i)     {        *iter = i;     } }   前向迭代器,是輸入、輸入迭代器的結合,但是卻沒能用有輸入、輸入迭代器的全部功能,真心覺得這個迭代器很尴尬。前向迭代器提取值的時候,要確保它是有效的迭代器(比如到了序列尾端),而輸出迭代器卻不用(輸出迭代器不提供比較操作,無需檢查是否達到尾端)。我沒見過比較有代表性的前向迭代器,所以給不出代碼示例(囧…)。   雙向迭代器,在前向迭代器的基礎上增加了回頭遍歷的能力。寫法上來說,就是提供了自減操作。最合適的列子非鏈表的迭代器莫屬了。如下:   #include <map> #include <set> #include <algorithm> #include <iterator> #include <string> using namespace std;       int main() {     list<int> lst;     for (int i = 0; i < 10; ++i)     {         lst.push_back(i);     }       list<int>::const_iterator iter(lst.begin());     while (iter != lst.end())     {         cout << *iter << " ";         ++iter;     }     cout << endl;       while (iter != lst.begin())     {         --iter;         cout << *iter << " ";     } }   隨機迭代器,在雙向迭代器的基礎上增加了隨機存取能力。寫法上來說,就是提供了加減法操作,還提供了大小比較操作(除了這個迭代器,其他都沒有大小比較,所以一般判斷迭代器是否結尾,是用 == 或者 != 來判斷)。最合適的列子就是vector的迭代器了。如下:   vector<int> vec; for (int i = 0; i < 10; ++i) {     vec.push_back(i); }   vector<int>::const_iterator iter(vec.begin()); cout << *(iter + 4) << endl;   至此,我們對C++迭代器有些基本的了解了。現在讓我們探索一下這背後到底是怎麼實現的。我們知道C++的STL是依靠模板(Template)來實現的,用C#的詞來描述就是泛型(Generic)。一個迭代器,其實是一個類型,一個遵循了一系列潛規則的類型。按照被潛的程度,分成兩種:自娛自樂,狼狽為奸。如果只是想自娛自樂的話,那麼很簡單,只要像下面這樣既可:   #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <algorithm> #include <iterator> #include <string> using namespace std;   template<typename Item> struct ListIter;   template<typename T> struct ListItem;     //作為存放元素的容器 template <typename T> struct ListContainer {     ListContainer() : _front(nullptr)                       , _end(nullptr)                       , _size(0)     {       }       void insert_front(T value)     {         ListItem<T>* newItem = new ListItem<T>(value, _front);           if (_front == nullptr)         {             _end = newItem;         }            _front = newItem;     }       void insert_end(T value)     {         ListItem<T>* newItem = new ListItem<T>(value, nullptr);         if (_end == nullptr)         {             _front = newItem;             _end = newItem;         }         else         {             _end->setNext(newItem);             _end = newItem;         }     }       void display(std::ostream &os = std::cout) const     {         ListItem<T>* tmp = _front;         while (tmp != nullptr)         {             os << tmp->value() << " ";             tmp = tmp->next();         }         os << std::endl;     }       ListItem<T>* front() const     {         return _front;     }   private:     ListItem<T>* _end;     ListItem<T>* _front;     long _size; };   //每個元素 template<typename T> struct ListItem {     ListItem(T val, ListItem<T>* next) : _value(val)                                         , _next(next)     {     }       T value() const     {         return _value;     }     ListItem* next() const     {         return _next;     }       void setNext(ListItem<T>* next)     {         _next = next;     } private:     T _value;     ListItem<T>* _next; };   //迭代器 template<typename Item> struct ListIter {     Item* ptr;       ListIter(Item* p = 0) : ptr(p)     {}       Item& operator*() const { return *ptr; }     Item* operator->() const { return ptr; }          ListIter& operator++()     {         ptr = ptr->next();         return *this;     }       ListIter operator++(int)     {         ListIter tmp =*this;         ++(*this);         return tmp;     }       bool operator==(const ListIter& i) const     {         return ptr == i.ptr;     }       bool operator!=(const ListIter& i) const     {         return ptr != i.ptr;     } };   int main() {     ListContainer<int> myList;       for (int i = 0; i < 10; ++i)     {         myList.insert_front(i);         myList.insert_end(i + 10);     }       myList.display();       ListIter<ListItem<int> > begin(myList.front());     ListIter<ListItem<int> > end;     while (begin != end)     {         cout << begin->value() << endl;         ++begin;     } }   上述代碼中,我們完全依賴自己的雙手,通過重載*、->、 ++、==、!=等操作符,實現了自己的行為上類似迭代器的迭代器。但是我們僅能自娛自樂而已,不能融入STL的大家庭。我們無法復用STL原有的輪子,也無法將我們的輪子完美的放進STL(只需重載一下全局的!=操作符,可以使用STL的find)。我們為了實現這個迭代器,將容器的元素類型(ListItem)暴露了,而且還暴露了ListItem的內部實現細節(重載++操作符,用到了ptr->next()),明顯不科學啊!所以一般迭代器都是相應的容器的設計者實現的,內嵌在容器中。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved