前幾天重看了C++ primer的第16章,正好同時也複習了樹的操作,於是寫了個二分查找樹的模板類。一開始挺順利,後來想嘗試一下使用函數指針來傳遞遍歷樹的函數,因此有了下面的問題,也從解決方法中學到了很多。
我想要的實現是這樣的:有一個TravelRecursive函數,專門負責用遞歸的方式遍歷整棵樹,而遍歷的方法(中序、前序、後序)想用函數指針傳進來。對結點的處理函數也想用函數指針傳進來。於是這樣寫:
//class BSTree
template <typename T> class BSTree
{
//member functions
private:
typedef void (BSTree<T>::*KF)(BSTNode<T>* node);
typedef void (BSTree<T>::*PF)(BSTNode<T>* node, KF processFun);
. . . . . .
public:
void TravelRecursive(PF pf, KF processFun);
void PreOderRecursive(BSTNode<T>* node, KF processFun); //前序遞歸遍歷函數
void process(BSTNode<T>* node); //處理結點的函數
}
先用typedef定義了函數指針類型KF和PF。KF和PF的前面之所以要加上BSTree<T>::的作用域,是因爲我希望這個函數指針指向的函數是這個模板類的函數成員,因此需要加上這個作用域。
然後,我們來看一下TravelRecursive函數是如何使用函數指針的。
一開始我是這樣寫的:
template <typename T> void BSTree<T>::TravelRecursive(PF pf, KF process)
{
pf(this->root, process);
}
然後這樣調用:tree.TravelRecursive(&BSTree<int>::PosOderRecursive, &BSTree<int>::process);
運行的時候就報錯了:error C2064: term does not evaluate to a function taking 1 arguments
這個錯誤單是看描述根本不明所以。後來請教了高手,高手只說了怎麼寫,但沒說爲什麼。於是自己想了一會,明白是哪裏出了問題。
回到函數TravelRecursive的調用,我傳入的是&BSTree<int>::PosOderRecursive,那麼PF指向的確是這個函數PosOderRecursive。但調用這個函數時,我寫的是pf(this->root, process),這時就會有問題了。我們平時調用函數的時候,總是a.fun(),這樣其實隱式地給fun函數傳入了一個this參數,而現在因爲傳的時候只用作用域的方式指明瞭函數的地址,所以如果只是pf(this->root, process)這樣去調用的話,會缺少this參數,因此就出現了上述的錯誤。
現在把代碼做如下的修改:
template <typename T> void BSTree<T>::TravelRecursive(PF pf, KF process)
{
(this->*pf)(this->root, process);
}
顯示地給pf指向的函數傳入this參數,這樣就能運行正確了。