2.2.2 第二版:工 業 時 代--組 件化 大 生 產
STL便是這個時 代的產 物,正如其他科 技成果一樣,C++程式員也應該努力使自己適應並充分利用這個"高科技成果"。讓我們重 新審 視第一版的那個 破 爛 不 堪的程式。試著使用一下STL,看看效果如何。
// name:example2_2.cpp// alias:The first STL program#include <iostream>#include <vector>#include <algorithm>using namespace std;void main(void){vector<int> num;// STL中的vector容器int element;// 從標準輸入裝置讀入整數, // 直到輸入的是非整型資料為止while (cin >> element)num.push_back(element);// STL中的排序演算法sort(num.begin(), num.end());// 將排序結果輸出到標準輸出裝置for (int i = 0; i < num.size(); i ++)cout << num[i] << "/n";}
STL便是這個時 代的產 物,正如其他科 技成果一樣,C++程式員也應該努力使自己適應並充分利用這個"高科技成果"。讓我們重 新審 視第一版的那個 破 爛 不 堪的程式。試著使用一下STL,看看效果如何。
// name:example2_2.cpp// alias:The first STL program#include <iostream>#include <vector>#include <algorithm>using namespace std;void main(void){vector<int> num;// STL中的vector容器int element;// 從標準輸入裝置讀入整數, // 直到輸入的是非整型資料為止while (cin >> element)num.push_back(element);// STL中的排序演算法sort(num.begin(), num.end());// 將排序結果輸出到標準輸出裝置for (int i = 0; i < num.size(); i ++)cout << num[i] << "/n";}
STL便是這個時 代的產 物,正如其他科 技成果一樣,C++程式員也應該努力使自己適應並充分利用這個"高科技成果"。讓我們重 新審 視第一版的那個 破 爛 不 堪的程式。試著使用一下STL,看看效果如何。
// name:example2_2.cpp// alias:The first STL program#include <iostream>#include <vector>#include <algorithm>using namespace std;void main(void){vector<int> num;// STL中的vector容器int element;// 從標準輸入裝置讀入整數, // 直到輸入的是非整型資料為止while (cin >> element)num.push_back(element);// STL中的排序演算法sort(num.begin(), num.end());// 將排序結果輸出到標準輸出裝置for (int i = 0; i < num.size(); i ++)cout << num[i] << "/n";}
STL便是這個時 代的產 物,正如其他科 技成果一樣,C++程式員也應該努力使自己適應並充分利用這個"高科技成果"。讓我們重 新審 視第一版的那個 破 爛 不 堪的程式。試著使用一下STL,看看效果如何。
// name:example2_2.cpp// alias:The first STL program#include <iostream>#include <vector>#include <algorithm>using namespace std;void main(void){vector<int> num;// STL中的vector容器int element;// 從標準輸入裝置讀入整數, // 直到輸入的是非整型資料為止while (cin >> element)num.push_back(element);// STL中的排序演算法sort(num.begin(), num.end());// 將排序結果輸出到標準輸出裝置for (int i = 0; i < num.size(); i ++)cout << num[i] << "/n";}
STL便是這個時 代的產 物,正如其他科 技成果一樣,C++程式員也應該努力使自己適應並充分利用這個"高科技成果"。讓我們重 新審 視第一版的那個 破 爛 不 堪的程式。試著使用一下STL,看看效果如何。
// name:example2_2.cpp// alias:The first STL program#include <iostream>#include <vector>#include <algorithm>using namespace std;void main(void){vector<int> num;// STL中的vector容器int element;// 從標準輸入裝置讀入整數, // 直到輸入的是非整型資料為止while (cin >> element)num.push_back(element);// STL中的排序演算法sort(num.begin(), num.end());// 將排序結果輸出到標準輸出裝置for (int i = 0; i < num.size(); i ++)cout << num[i] << "/n";}
STL便是這個時 代的產 物,正如其他科 技成果一樣,C++程式員也應該努力使自己適應並充分利用這個"高科技成果"。讓我們重 新審 視第一版的那個 破 爛 不 堪的程式。試著使用一下STL,看看效果如何。
// name:example2_2.cpp// alias:The first STL program#include <iostream>#include <vector>#include <algorithm>using namespace std;void main(void){vector<int> num;// STL中的vector容器int element;// 從標準輸入裝置讀入整數, // 直到輸入的是非整型資料為止while (cin >> element)num.push_back(element);// STL中的排序演算法sort(num.begin(), num.end());// 將排序結果輸出到標準輸出裝置for (int i = 0; i < num.size(); i ++)cout << num[i] << "/n";}
這個程式的主要部分改用了STL的組件,看起來要比第一個程式簡潔一點,你已經找不到那個討厭的compare函數了。它真的能很好的運行嗎?你可以試試,因為程式的運行結果和前面的大致差不多,所以在此略去。我可以向你保證,這個程式是足夠健壯的。不過,可能你還沒有完全看明白程式的代碼,所以我需要為你解釋一下。畢竟,這個戲法變得太快了,較之第一個程式,一眨眼的功夫,那些老的C++程式員所熟悉的代碼都不見了,取而代之的是一些新鮮玩意兒。
程式的前三行是包含的標頭檔,它們提供了程式所要用到的所有C++特性(包括輸入輸出處理,STL中的容器和演算法)。不必在意那個.h,並不是我的疏忽,程式保證可以編譯通過,只要你的C++編譯器支援標準C++規範的相關部分。你只需要把它們看作是一些普通的C++標頭檔就可以了。事實上,也正是如此,如果你對這個變化細節感興趣的化,可以留意一下你身旁的佐餐。
同樣可以忽略第四行的存在。加入那個聲明只是為了表明程式引用到了std這個標準名字空間(namespace),因為STL中的那些玩意兒全都包含在那裡面。只有通過這行聲明,編譯器才能允許你使用那些有趣的特性。
程式中用到了vector,它是STL中的一個標準容器,可以用來存放一些元素。你可以把vector理解為int [?],一個整型的數組。之所以大小未知是因為,vector是一個可以動態調整大小的容器,當容器已滿時,如果再放入元素則vector會悄悄擴大自己的容量。push_back是vector容器的一個類屬成員函數,用來在容器尾端插入一個元素。main函數中第一個while迴圈做的事情就是不斷向vector容器尾端插入整型資料,同時自動維護容器空間的大小。
sort是STL中的標準演算法,用來對容器中的元素進行排序。它需要兩個參數用來決定容器中哪個範圍內的元素可以用來排序。這裡用到了vector的另兩個類屬成員函數。begin()用以指向vector的首端,而end()則指向vector的末端。這裡有兩個問題,begin()和end()的傳回值是什嗎?這涉及到STL的另一個重要組件--迭代器(Iterator),不過這裡並不需要對它做詳細瞭解。你只需要把它當作是一個指標就可以了,一個指向整型資料的指標。相應的sort函式宣告也可以看作是void sort(int* first, int* last),儘管這實際上很不精確。另一個問題是和end()函數有關,儘管前面說它的傳回值指向vector的末端,但這種說法不能算正確。事實上,它的傳回值所指向的是vector中最末端元素的後面一個位置,即所謂pass-the-end value。這聽起來有點費解,不過不必在意,這裡只是稍帶一提。總的來說,sort函數所做的事情是對那個准整型數組中的元素進行排序,一如第一個程式中的那個qsort,不過比起qsort來,sort似乎要簡單了許多。
程式的最後是輸出部分,在這裡vector完全可以以假亂真了,它所提供的對元素的訪問方式簡直和普通的C++內建數組一模一樣。那個size函數用來返回vector中的元素個數,就相當於第一個程式中的變數n。這兩行代碼直觀的不用我再多解釋了。
我想我的耐心講解應該可以使你大致看懂上面的程式了,事實上STL的運用使程式的邏輯更加清晰,使代碼更易於閱讀。試問,有誰會不明白begin、end、size這樣的字眼所表達的含義呢(除非他不懂英語)?試著運行一下,看看效果。再試著多輸入幾個數,看看是否會發生數組越界現象。實踐證明,程式運行良好。是的,由於vector容器自我維護了自身的大小,C++程式員就不用操心動態記憶體分配了,指標的錯誤使用畢竟會帶來很多麻煩,同時程式也會變得冗長無比。這正是前面第三種方案的缺點所在。
再仔細審視一下你的第一個STL版的C++程式,回顧一下第一章所提到的那些有關STL的優點:便於使用,具有工業強度……,再比較一下第一版的程式,我想你應該有所體會了吧!
2.2.3 第三版:唯美主義的傑作
事態的發展有時候總會趨向極端,這在那些唯美主義者當中猶是如此。首先聲明,我並不是一個唯美主義者,提供第二版程式的改進版,完全是為了讓你更深刻的感受到STL的魅力所在。在看完第三版之後,你會強烈感受到這一點。或許你也會變成一個唯美主義者了,至少在STL方面。這應該不是我的錯,因為決定權在你手裡。下面我們來看看這個絕版的C++程式。
// name:example2_3.cpp// alias:aesthetic version#include <iostream>#include <vector>#include <algorithm>#include <iterator>using namespace std;void main(void){typedef vector<int>int_vector;typedef istream_iterator<int>istream_itr;typedef ostream_iterator<int>ostream_itr;typedef back_insert_iterator< int_vector >back_ins_itr;// STL中的vector容器int_vector num;// 從標準輸入裝置讀入整數, // 直到輸入的是非整型資料為止copy(istream_itr(cin), istream_itr(), back_ins_itr(num));// STL中的排序演算法sort(num.begin(), num.end());// 將排序結果輸出到標準輸出裝置copy(num.begin(), num.end(), ostream_itr(cout, "/n"));} 在這個程式裡幾乎每行代碼都是和STL有關的(除了main和那對花括弧,當然還有注釋),並且它包含了STL中幾乎所有的各大組件(容器container,迭代器iterator, 演算法algorithm, 適配器adaptor),唯一的遺憾是少了函數對象(functor)的身影。
還記得開頭提到的一個典型系統所具有的基本特徵嗎?--輸入+處理+輸出。所有這些功能,在上面的程式裡,僅僅是通過三行語句來實現的,其中每一行語句對應一種操作。對於資料的操作被高度的抽象化了,而演算法和容器之間的組合,就像搭積木一樣輕鬆自如,系統的耦合度被降到了極低點。這就是閃耀著泛型之光的STL的偉大力量。如此簡潔,如此巧妙,如此神奇!就像魔術一般,以至於再一次讓你摸不著頭腦。怎麼實現的?為什麼在看第二版程式的時候如此清晰的你,又墜入了五裡霧中(竊喜)。
請留意此處的標題(唯美主義的傑作),在實際環境中,你未必要做到這樣完美。畢竟美好願望的破滅,在生活中時常會發生。過於理想化,並不是一件好事,至少我是這麼認為的。正如前面提到的,這個程式只是為了展示STL的獨特魅力,你不得不為它的出色表現所折服,也許只有深諳STL之道的人才會想出這樣的玩意兒來。如果你只是一般性的使用STL,做到第二版這樣的程度也就可以了。
實在是因為這個程式太過"簡單",以至於我無法肯定,在你還沒有完全掌握STL之前,通過我的講解,是否能夠領會這區區三行代碼,我將盡我的最大努力。
前面提到的迭代器可以對容器內的任意元素進行定位和訪問。在STL裡,這種特性被加以推廣了。一個cin代表了來自輸入裝置的一段資料流,從概念上講它對資料流的訪問功能類似於一般意義上的迭代器,但是C++中的cin在很多地方操作起來並不像是一個迭代器,原因就在於其介面和迭代器的介面不一致(比如:不能對cin進行++運算,也不能對之進行取值運算--即*運算)。為瞭解決這個矛盾,就需要引入適配器的概念。istream_iterator便是一個適配器,它將cin進行封裝,使之看起來像是一個普通的迭代器,這樣我們就可以將之作為實參傳給一些演算法了(比如這裡的copy演算法)。因為演算法只認得迭代器,而不會接受cin。對於上面程式中的第一個copy函數而言,其第一個參數展開後的形式是:istream_iterator(cin),其第二個參數展開後的形式是:istream_iterator()(如果你對typedef的文法不清楚,可以參考有關的c++語言書籍)。其效果是產生兩個迭代器的臨時對象,前一個指向整型輸入資料流的開始,後一個則指向"pass-the-end value"。這個函數的作用就是將整型輸入資料流從頭至尾逐一"拷貝"到vector這個准整型數組裡,第一個迭代器從開始位置每次累進,最後到達第二個迭代器所指向的位置。或許你要問,如果那個copy函數的行為真如我所說的那樣,為什麼不寫成如下這個樣子呢?
copy(istream_iterator<int>(cin), istream_iterator<int>(), num.begin());
你確實可以這麼做,但是有一個小小的麻煩。還記得第一版程式裡的那個數組越界問題嗎?如果你這麼寫的話,就會遇到類似的麻煩。原因在於copy函數在"拷貝"資料的時候,如果輸入的資料個數超過了vector容器的範圍時,資料將會拷貝到容器的外面。此時,容器不會自動成長容量,因為這隻是簡單地拷貝,並不是從末端插入。為瞭解決這個問題,另一個適配器back_insert_iterator登場了,它的作用就是引導copy演算法每次在容器末端插入一個資料。程式中的那個back_ins_itr(num)展開後就是:back_insert_iterator(num),其效果是產生一個這樣的迭待器對象。
終於將講完了三分之一(真不容易!),好在第二句和前一版程式沒有差別,這裡就略過了。至於第三句,ostream_itr(cout, "/n")展開後的形式是:ostream_iterator(cout, "/n"),其效果是產生一個處理輸出資料流的迭待器對象,其位置指向資料流的起始處,並且以"/n"作為分割符。第二個copy函數將會從頭至尾將vector中的內容"拷貝"到輸出裝置,第一個參數所代表的迭代器將會從開始位置每次累進,最後到達第二個參數所代表的迭代器所指向的位置。
這就是全部的內容。
事態的發展有時候總會趨向極端,這在那些唯美主義者當中猶是如此。首先聲明,我並不是一個唯美主義者,提供第二版程式的改進版,完全是為了讓你更深刻的感受到STL的魅力所在。在看完第三版之後,你會強烈感受到這一點。或許你也會變成一個唯美主義者了,至少在STL方面。這應該不是我的錯,因為決定權在你手裡。下面我們來看看這個絕版的C++程式。
// name:example2_3.cpp// alias:aesthetic version#include <iostream>#include <vector>#include <algorithm>#include <iterator>using namespace std;void main(void){typedef vector<int>int_vector;typedef istream_iterator<int>istream_itr;typedef ostream_iterator<int>ostream_itr;typedef back_insert_iterator< int_vector >back_ins_itr;// STL中的vector容器int_vector num;// 從標準輸入裝置讀入整數, // 直到輸入的是非整型資料為止copy(istream_itr(cin), istream_itr(), back_ins_itr(num));// STL中的排序演算法sort(num.begin(), num.end());// 將排序結果輸出到標準輸出裝置copy(num.begin(), num.end(), ostream_itr(cout, "/n"));} 在這個程式裡幾乎每行代碼都是和STL有關的(除了main和那對花括弧,當然還有注釋),並且它包含了STL中幾乎所有的各大組件(容器container,迭代器iterator, 演算法algorithm, 適配器adaptor),唯一的遺憾是少了函數對象(functor)的身影。
還記得開頭提到的一個典型系統所具有的基本特徵嗎?--輸入+處理+輸出。所有這些功能,在上面的程式裡,僅僅是通過三行語句來實現的,其中每一行語句對應一種操作。對於資料的操作被高度的抽象化了,而演算法和容器之間的組合,就像搭積木一樣輕鬆自如,系統的耦合度被降到了極低點。這就是閃耀著泛型之光的STL的偉大力量。如此簡潔,如此巧妙,如此神奇!就像魔術一般,以至於再一次讓你摸不著頭腦。怎麼實現的?為什麼在看第二版程式的時候如此清晰的你,又墜入了五裡霧中(竊喜)。
請留意此處的標題(唯美主義的傑作),在實際環境中,你未必要做到這樣完美。畢竟美好願望的破滅,在生活中時常會發生。過於理想化,並不是一件好事,至少我是這麼認為的。正如前面提到的,這個程式只是為了展示STL的獨特魅力,你不得不為它的出色表現所折服,也許只有深諳STL之道的人才會想出這樣的玩意兒來。如果你只是一般性的使用STL,做到第二版這樣的程度也就可以了。
實在是因為這個程式太過"簡單",以至於我無法肯定,在你還沒有完全掌握STL之前,通過我的講解,是否能夠領會這區區三行代碼,我將盡我的最大努力。
前面提到的迭代器可以對容器內的任意元素進行定位和訪問。在STL裡,這種特性被加以推廣了。一個cin代表了來自輸入裝置的一段資料流,從概念上講它對資料流的訪問功能類似於一般意義上的迭代器,但是C++中的cin在很多地方操作起來並不像是一個迭代器,原因就在於其介面和迭代器的介面不一致(比如:不能對cin進行++運算,也不能對之進行取值運算--即*運算)。為瞭解決這個矛盾,就需要引入適配器的概念。istream_iterator便是一個適配器,它將cin進行封裝,使之看起來像是一個普通的迭代器,這樣我們就可以將之作為實參傳給一些演算法了(比如這裡的copy演算法)。因為演算法只認得迭代器,而不會接受cin。對於上面程式中的第一個copy函數而言,其第一個參數展開後的形式是:istream_iterator(cin),其第二個參數展開後的形式是:istream_iterator()(如果你對typedef的文法不清楚,可以參考有關的c++語言書籍)。其效果是產生兩個迭代器的臨時對象,前一個指向整型輸入資料流的開始,後一個則指向"pass-the-end value"。這個函數的作用就是將整型輸入資料流從頭至尾逐一"拷貝"到vector這個准整型數組裡,第一個迭代器從開始位置每次累進,最後到達第二個迭代器所指向的位置。或許你要問,如果那個copy函數的行為真如我所說的那樣,為什麼不寫成如下這個樣子呢?
copy(istream_iterator<int>(cin), istream_iterator<int>(), num.begin());
你確實可以這麼做,但是有一個小小的麻煩。還記得第一版程式裡的那個數組越界問題嗎?如果你這麼寫的話,就會遇到類似的麻煩。原因在於copy函數在"拷貝"資料的時候,如果輸入的資料個數超過了vector容器的範圍時,資料將會拷貝到容器的外面。此時,容器不會自動成長容量,因為這隻是簡單地拷貝,並不是從末端插入。為瞭解決這個問題,另一個適配器back_insert_iterator登場了,它的作用就是引導copy演算法每次在容器末端插入一個資料。程式中的那個back_ins_itr(num)展開後就是:back_insert_iterator(num),其效果是產生一個這樣的迭待器對象。
終於將講完了三分之一(真不容易!),好在第二句和前一版程式沒有差別,這裡就略過了。至於第三句,ostream_itr(cout, "/n")展開後的形式是:ostream_iterator(cout, "/n"),其效果是產生一個處理輸出資料流的迭待器對象,其位置指向資料流的起始處,並且以"/n"作為分割符。第二個copy函數將會從頭至尾將vector中的內容"拷貝"到輸出裝置,第一個參數所代表的迭代器將會從開始位置每次累進,最後到達第二個參數所代表的迭代器所指向的位置。
這就是全部的內容。
2.3 曆史的評價
曆史的車輪總是滾滾向前的,工業時代的文明較之史前時代,當然是先進並且發達的。回顧那兩個時代的C++程式,你會真切的感受到這種差別。簡潔易用,具有工業強度,較好的可移植性,高效率,加之第三個令人目眩的絕版程式所體現出來的高度抽象性,高度靈活性和組件化特性,使你對STL背後所蘊含的泛型化思想都有了些微的感受。
真幸運,你可以橫跨兩個時代,有機會目睹這種"文明"的差異。同時,這也應該使你越加堅定信念,使自己順應時代的潮流。
曆史的車輪總是滾滾向前的,工業時代的文明較之史前時代,當然是先進並且發達的。回顧那兩個時代的C++程式,你會真切的感受到這種差別。簡潔易用,具有工業強度,較好的可移植性,高效率,加之第三個令人目眩的絕版程式所體現出來的高度抽象性,高度靈活性和組件化特性,使你對STL背後所蘊含的泛型化思想都有了些微的感受。
真幸運,你可以橫跨兩個時代,有機會目睹這種"文明"的差異。同時,這也應該使你越加堅定信念,使自己順應時代的潮流。
2.4 如何運行
在你還沒有真正開始運行前面後兩個程式之前,最好先瀏覽一下本節。這裡簡單介紹了在特定編譯器環境下運行STL程式的一些細節,並提供了一些可能遇到的問題的解決辦法。
此處,我選用了目前在Windows平台下較為常見的Microsoft Visual C++ 6.0和Borland C++ Builder 6.0作為例子。儘管Visual C++ 6.0對最新的ANSI/ISO C++標準支援的並不是很好。不過據稱Visual C++ .NET(也就是VC7.0)在這方面的效能有所改善。
你可以選用多種方式運行前面的程式,比如在Visual C++下,你可以直接在DOS命令列狀態下編譯運行,也可以在VC的IDE下採用控制台應用程式(Console Application)的方式運行。對於C++ Builder,情況也類似。
對於Visual C++而言,如果是在DOS命令列狀態下,你首先需要找到它的編譯器。假定你的Visual C++裝在C:/Program Files/Microsoft Visual Studio/VC98下面,則其編譯器所在路徑應該是C:/Program Files/Microsoft Visual Studio/VC98/Bin,在那裡你可以找到cl.exe檔案。編譯時間請加上/GX和/MT參數。如果一切正常,結果就會產生一個可執行檔。如下所示:
cl /GX /MT example2_2.cpp
前一個參數用於告知編譯器允許異常處理(Exception Handling)。在P. J. Plauger STL中的很多地方使用了異常處理機制(即try…throw…catch文法),所以應該加上這個參數,否則會有如下警告資訊:
warning C4530: C++ exception handler used, but unwind semantics are not enabled.
後一個參數則用於使程式支援多線程,它需要在連結時使用LIBCMT.LIB庫檔案。不過P. J. Plauger STL並不是安全執行緒的(thread safety)。如果你是在VC環境下使用像STLport這樣的STL實現版本,則需要加上這個參數,因為STLport是安全執行緒的。
如果在IDE環境下,可以在建立工程的時候選擇控制台應用程式。
圖3:在Visual C++ IDE環境下運行STL程式
至於那些參數的設定,則可以通過在Project功能功能表項目中的Settings功能【Alt+F7】中設定編譯選項來完成。
圖4:在Visual C++ IDE環境下設定編譯參數
有時,在IDE環境下編譯STL程式時,可能會出現如下警告資訊(前面那幾個樣本程式不會出現這種情況):
warning C4786: '……' : identifier was truncated to '255' characters in the debug information
這是因為編譯器在Debug狀態下編譯時間,把程式中所出現的標識符長度限制在了255個字元範圍內。如果超過最大長度,這些標識符就無法在調試階段查看和計算了。而在STL程式中大量的用到了模板函數和模板類,編譯器在執行個體化這些內容時,展開之後所產生的標識符往往很長(沒準會有一千多個字元!)。如果你想認識一下這個warning的話,很簡單,在程式裡加上如下一行代碼:
vector<string>string_array;// 類似於字串陣列變數
對於這樣的warning,當然可以置之不理,不過也是有解決辦法的。 你可以在檔案開頭加入下面這一行:#pragma warning(disable: 4786)。它強制編譯器忽略這個警告資訊,這種做法雖然有點粗魯,但是很有效。
至於C++ Builder,其DOS命令列狀態下的運行方式是這樣的。假如你的C++ Builder裝在C:/Program Files/Borland/CBuilder6。則其編譯器所在路徑應該是C:/Program Files/ Borland/CBuilder6/Bin,在那裡你可以找到bcc32.exe檔案,輸入如下命令,即大功告成了:
bcc32 example2_2.cpp
至於IDE環境下,則可以在建立應用程式的時候,選擇控制台嚮導(Console Wizard)。
圖5:在C++ Builder IDE環境下運行STL程式
現在你可以在你的機器上運行前面的樣本程式了。不過,請恕我多嘴,有些細節不得不提請你注意。小心編譯器給你留下的陷阱。比如前面第三個程式中有如下這一行代碼:
typedef back_insert_iterator< int_vector >back_ins_itr;
請留意">"前面的空格,最好不要省去。如果你吝惜這點空格所佔用的磁碟空間的話,那就太不划算了。其原因還是在於C++編譯器本身的缺陷。上述代碼,相當於如下代碼(編譯器做的也正是這樣的翻譯工作):
typedef back_insert_iterator< vector<int> >back_ins_itr;
如果你沒有加空格的話,編譯器會把">>"誤認為是單一標識(看起來很像那個資料流輸入操作符">>")。為了迴避這個難題,C++要求使用者必須在兩個右角括弧之間插入空格。所以,你最好還是老老實實照我的話做,以避免不必要的麻煩。不過有趣的是,對於上述那行展開前的代碼,在Visual C++裡即使你沒有加空格,編譯器也不會報錯。而同樣的代碼在C++ Builder中沒有那麼幸運了。不過,最好還是不要心存僥倖,如果你採用展開後的書寫方式,則兩個編譯器都不會給你留情面了。
好了,請原諒我的絮叨,現在你可以親身感受一下STL所帶給你的真正獨特魅力了,祝你好運!