一般講排序演算法的文章,為了方便說明演算法本身,待排序元素的型別一般使用整型。還有些文章講泛型排序,待排序元素可以是任意型別,但對於待排序序列,卻一般只支援某一種儲存形式,比如定長陣列,比如std::vector,但不能同時支援它們。那麼我們有沒有辦法使用泛型技術即支援任意元素型別又支援大多數常用的序列型別進行排序呢?
1. 現有的泛型排序
我們知道stl支援幾種泛型排序,像sort,stable_sort,partial_sort,list::sort,但是它們都有一些限制。
- sort和partial_sort只支援支援隨機訪問迭代器randomaccessiterator的序列,像vector,但不支援list。
- list::sort只支援list排序。
- stable_sort支援支援雙向迭代器bidirectionaliterator的序列,如vector和list等,但不能支援陣列(包括指標定址的)序列,比如下面的陣列a和指標p所能訪問的序列。
int a[100];
myclass * p = new myclass[100];
網上也有一些文章,寫如何支援泛型排序,如這篇,但裡面所指的泛型是指待排序序列中元素型別的泛型,而不是指待排序序列的泛型。對於待排序序列,它只支援陣列儲存形式(t a),不支援vector和list等儲存形式(如:std::vector& a)。
2. 我們的目標
我們希望c++排序演算法能支援多種序列型別(當然,序列中的元素型別是任意的),排序函式的原型最好能像下面的宣告一樣,其中arr為任意型別的序列,size為序列的長度,ascending為公升序的標誌,降序時設為false。
void sort(a arr, int size, bool ascending = true);
3. 實現
a arr的宣告有點像動態型別語言?是。不過c++不能支援執行時的動態型別推導。我們還是得使用編譯期型別推導的template(什麼?c++11標準裡有自動型別推導?那也許將來我們就不需要這篇文章了)。
我們宣告乙個排序器的模板基類,如下:
template
class sorter
public:
sorter(void) {}
virtual ~sorter(void) {}
virtual void sort(a arr, int size, bool ascending = true) = 0;
private:
sorter(sorter& sorter);
sorter& operator=(const sorter& sorter);
簡單說說此類。sorter的模板引數a就是序列型別,後面我們將看到它是如何被應用的。之所以宣告乙個基類,是為了實現策略模式。之所以sort函式被宣告為純虛函式,目的是為了讓各種派生的排序演算法類過載它。宣告sorter類的複製構造和賦值操作符而不定義它們,是為了避免sorter子類物件間無意的複製和賦值(參見《effective c++》條款06)。在實踐中,有時我們會根據不同時間和空間的要求,使用不同排序演算法,所以,以上**是為了支援策略模式而存在的,不需要策略模式,不用宣告這個基類。這部分不是本文主要要討論的內容,我們來看看本文的重點。
下面我們以插入排序為例,說明如何實現泛型的排序器。
template
class insertionsorter : public sorter
public:
insertionsorter(void) {}
virtual ~insertionsorter(void) {}
virtual void sort(a arr, int size, bool ascending = true)
for (int j = 1;j < (int)size;j++)
t key = arr[j];
int i = j-1;
while (i >= 0 && (ascending?(arr[i] > key):(key > arr[i])))
arr[i+1] = arr[i];
i--;
arr[i+1] = key;
這個類又增加了乙個模板引數t,這個就是序列中元素的型別,有了t,我們的排序就可以支援任意元素型別(只要t支援「大於」操作「>」和「賦值」操作符「=」)。另一方面,從上面的**可以看到,序列arr用定址,這樣所有支援操作符的序列型別,比如:陣列、指標陣列、std::vector、std::deque,mfc的carray,它們都已過載了操作符,就都能使用這個排序函式來排序。
舉幾個例子,來展現不同序列型別如何使用這個進行排序(以下myclass為自定義的乙個類,即編譯期替換t的型別,讀者可以換成任何其它型別,但無論用什麼型別,必須支援操作符「>」和「=」)。
為指標陣列排序(myclass *實際上就是a的編譯期型別):
myclass * pmyclass = new myclass[100];
// 為pmyclass賦值**省略
sorter * pmyclasssorterpointer = new insertionsorter;
pmyclasssorterpointer->sort(pmyclass, size);
delete pmyclasssorterpointer;
為std::vector序列排序(std::vector&實際上就是a的編譯期型別):
std::vector vecmyclass;
// 為vecmyclass賦值**省略
sorter<:vector>&> * pmyclasssortervector = new insertionsorter&>;
pmyclasssorterpointer->sort(vecmyclass, vecmyclass.size());
delete pmyclasssortervector;
大家可能會說:std::list沒有過載操作符,無法使用這個泛型排序。是的,要想用這個泛型排序,序列型別必須支援。解決辦法是,我們繼承list類,並讓其過載操作符,**如下:
template
class mylist : public std::list
public:
t& operator(size_type _pos)
int i = 0;
iterator iter = begin();
while (iter != end())
if (_pos == i) break;
iter++;
i++;
return *iter;
const t& operator(size_type _pos) const
int i = 0;
const_iterator iter = begin();
while (iter != end())
if (_pos == i) break;
iter++;
i++;
C語言泛型程式設計 泛型氣泡排序
在實際程式設計中,常常會需要一些方法 函式 比如排序,它們具體實現基本一致,僅僅只有引數型別不同,那麼可不可以有一種通用的函式,不管是什麼型別的引數都可以通用呢?泛型程式設計 泛型即是指具有在多種資料型別上皆可操作的含義,與模板有些相似。利用泛型程式設計,我們可以寫一些通用的函式,以減少 量,實現 ...
C語言泛型程式設計 泛型氣泡排序
在實際程式設計中,常常會需要一些方法 函式 比如排序,它們具體實現基本一致,僅僅只有引數型別不同,那麼可不可以有一種通用的函式,不管是什麼型別的引數都可以通用呢?泛型程式設計 泛型即是指具有在多種資料型別上皆可操作的含義,與模板有些相似。利用泛型程式設計,我們可以寫一些通用的函式,以減少 量,實現 ...
C 泛型的氣泡排序
1.普通的整型氣泡排序 void bubblesort int a,int size template 泛型可指定公升降序的氣泡排序 void sortbubble t a,int size,bool ret 1 測試上述函式 int main sortbubble arr,sizeof arr s...