关于类成员函数指针的调用问题
在学习二叉树的时候,定义了一个二叉树类BinTree<T>。类中定义了按不同顺序遍历二叉树的成员函数(如前序遍历函数PreOrder(void(*visit)(BTNode<T>* p))),其形参列表中使用了一个指向访问函数Travse(BTNode<T>* p)的*visit指针。当按照如下方式定义类的两个成员函数PreOrder、Travse时:
template<class T>
struct BTNode
{
T data;
BTNode<T>* leftChild,
* rightChild;
//…
};
template<class T>
class BinTree
{
BTNode<T>* root;//二叉树根结点
//…
Public:
void PreOrder(void(*visit)(BTNode<T>* p))
{
Visit(root);//访问根结点
递归地访问左子树各结点
递归地访问右子树各结点
//…
}
void /*static _cdecl */Travse(BTNode<T>* p) //访问输出节点数据信息
{
if(p!=NULL)
cout<<p->data<<endl;
}
Void PreDisplay(){PreOrder(Travse);}//按照前序遍历的方法访问二叉树
//…
};
int main()
{
BinTree<int>Bt(-100);
Bt.PreDisplay();
return 0;
}
却出现了编译错误:
error C2664: 'void __thiscall BinTree<int>::PreOrder(void (__cdecl *)(struct BTNode<int> *))' : cannot convert parameter 1 from 'void (struct BTNode<int> *)' to 'void (__cdecl *)(struct BTNode<int> *)'
None of the functions with this name in scope match the target type
这说明void PreOrder(void(*visit)(BTNode<T>* p))调用的函数参数类型不对。改变函数的类型不能去掉错误,但是将这几个函数从类的定义中拿出来当作普通的函数指针使用不作任何改变就可以消除错误通过编译:
#include<iostream>
using namespace std;
void PreOrder(void(*f2)()) { f2();}
void Travse() { cout<<"成功调用:/nBinTree::Travse()"<<endl;}
void PreDisplay() { PreOrder(Travse);}
int main( )
{
PreDisplay();
return 0;
}
结果:成功调用:
BinTree::Travse()
上述情况编译不能通过的原因表面上并不在于函数类型调用不对,而是与 “类”有关。没通过编译的情况是用函数指针调用了 “类”的成员函数,通过编译的是用函数指针调用了非成员函数,而函数的类型完全相同[1]。这也说明了类的成员函数指针和普通的函数指针是不一样的,与一般函数指针不同:指向“类”的成员函数的指针不仅包含成员函数地址的信息,而且包含与类的属性有关的信息,因此,一般函数指针和类的成员函数指针是根本不同的两种类型,当然,也就不能用一般函数指针直接调用类的成员函数,编译出错的原因。然而较早版本的编译软件是可以通过编译的[2]。
解决类的成员函数指针可以有几种方法。最简单的一种是将函数指针指向的调用函数定义为static类型的,static 类型的成员函数与类是分开的,其函数指针也不包含对象信息,与一般函数指针一致。然而,函数指针指向的被调用成员函数的定义内不能出现任何类的成员(包括变量和函数)。于是上述代码中只修改下面一部分问题即可解决:
void static _cdecl Travse(BTNode<T>* p) //访问输出节点数据信息
{
if(p!=NULL)
cout<<p->data<<endl;
}
还有一种办法是使用一个全局函数为中转函数,间接调用类的成员函数:
template<class T>
class BinTree<T>;
void __cdecl Helper(BinTree<T> * Bt);
class BinTree
{
public:
//…
void PreOrder( void (* pf)( BinTree<T>*)) { pf(this) ;}
void Travse( ) { cout<<"成功调用:/nBinTree::Travse()"<<endl; }
void PreDisplay(){PreOrder ( Helper);}
//…
};
void __cdecl Helper(BinTree<T> * Bt)
{
Bt-> Travse();
};
这个方法对成员函数没有任何要求。
[参考文献]:
[1].eros.类的成员函数指针(比较深入)
[2].zhouhuahai. C++指针直接调用类成员函数探讨(转载)