泛型編程

泛型編程

Bjarne: 如果你面對的問題既需要某些運行期決議(需要面向對象編程),又具有一些能夠從編譯期決議中獲益的方面(泛型編程的用武之地)的話,那麼你就需要將面向對象編程和泛型編程結合起來。例如,面向對象編程的經典例子 — 將一個保存了shape的容器中的所有元素都顯示出來就屬於這類問題。幾十年前我第一次在Simula中看到過這個例子,後來直到遇到了泛型編程,我纔看到它的改進實現。考慮以下代碼:

void draw_all(vector<Shape*>& vs)
{
for (int i=0; i<vs.size(); ++i) vs[i]->draw();
}


我猜想這並不能被看作純粹的面向對象編程,因爲我直接利用了“vs是一個裝有Shape*元素的vector”這個事實。畢竟,類型的參數化通常是被認爲屬於泛型編程的範疇。我們也可以消除這種對靜態類型信息的使用(所謂“不純粹的面向對象編程”):

void draw_all(Object* container)
{
Vector* v = dynamic_cast<Vector*>(container);
for (int i=0; i<v.size(); ++i)
{
Shape* ps = dynamic_cast<Shape*>(v[i]);
ps->draw();
}
}


但凡鼓勵以上這種風格的語言,其語法通常都比較漂亮,然而這個例子卻說明了當你把靜態類型信息的使用減至最小的時候發生了什麼。如今,在C++或其它語言中,仍然有人在使用這種風格。我只是希望他們在錯誤處理方面有系統化的準備。

在前一個例子中,vector<Shap*>依賴於對泛型編程的一個最簡單的運用:vector的元素類型被參數化了,而且我們的示例代碼正獲益於此。在這個方向上我們還可以走得更遠,即推而廣之到所有標準庫容器身上:

template<class Container> void draw_all(Container& cs)
{
for (typename C::iterator p=cs.begin(); p!=cs.end(); ++p)
(*p)->draw();
}


例如,這段代碼就既可以作用於vector上,又可以作用於list上。編譯期決議確保我們不用爲這種泛化處理付出任何運行期額外代價。我們還可以通過在draw_all()的使用接口中運用迭代器,從而進行進一步的泛化處理:

template<class Iter> void draw_all(Iter fist, Iter last)
{
for (; first!=last; ++first)
(*first)->draw();
}


這就使內建數組類型都得到了支持:

Shape* a[max];
// 向a中填充Shape*類型的元素
draw_all(a,a+max);


我們還可以結合運用標準庫算法for_each()和函數適配器mem_fun()來消除顯式的循環:

template<class Iter> void draw_all(Iter fist, Iter last)
{
for_each(first, last, mem_fun(&Shape::draw);
}


在這些例子中,我們結合了面向對象(對虛函數draw()的調用以及對類繼承體系的假設)和泛型編程(參數化的容器和算法)技術。我看不出如果這兩種編程風格(即所謂的“範型”)各自獨立運用如何達到同樣好的效果。這也是一個簡單的“多範型編程”的例子。

我認爲在設計和編程技術方面,我們還需要做更多的工作,以便確定出“關於何時採用哪種範型以及如何結合運用它們”的更爲具體的規則。爲此,我們還需要一個比“多範型編程”更好的名字。

注意,這也是一個關於編譯錯誤信息變得可怕的例子,因爲我們並沒有顯式地表達出我們的假設。例如,我們假設容器裏的元素類型爲Shape*,然而在代碼中,這個假設卻相當隱晦。這種情況下我們可以使用約束類(此處爲Point_to):

template<class Iter> void draw_all(Iter fist, Iter last)
{
Points_to<Iter,Shape*>();
for_each(first, last, mem_fun(&Shape::draw);
}


然而我們又確實很想說明“first和last必須爲前向迭代器”:

template<Forward_iterator<Shape*> Iter>
void draw_all(Iter fist, Iter last)
{
for_each(first, last, mem_fun(&Shape::draw);
}


這是“concepts”可以大展拳腳的地方之一。

;-)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章