如果有一篇好文章
在编程工作中,我们经常会遇到通过“类”中的函数指针调用成员函数的需求。 例如,在类中使用C++标准库中的排序函数qsort时,qsort参数需要一个“比较函数”。 指针,如果这个“类”使用了一个成员函数作为“比较函数”,则需要将这个成员函数的指针传递给qsort以供其调用。 本文讨论的使用指针调用“类”的成员函数包括以下三种情况:
(1). 将“类”的成员函数指针赋值给同类型的非成员函数指针,如:
实施例1
(2)在一个“类”中,有标准库函数,例如qsort,或者其他全局函数,它们使用函数指针来调用该类的成员函数。 喜欢:
示例2:
(3)同一个“类”中,一个成员函数调用另一个成员函数,如:
示例3:
上述三种情况的代码语法都没有明显的错误。 在一些早期的编译环境中,比如VC++ 4.0,编译通常可以通过,或者最多会给出一个问题提醒()。 后来的编译工具,如VC++6.0等一些常用的C++编译软件,无法通过上述代码的编译,并指出错误如下(以VC++6.0编译的第三种情况为例):
错误 C2664: '' : 1 从 'void (void)' 到 'void ( *)(void)'
范围内具有此名称的内容均不与类型匹配
即:参数中调用的函数类型不正确。
根据上面的提示,仅通过改变函数的类型并不能消除错误。 但是,如果将这些函数从类定义中取出,则无需任何更改即可消除错误。 通过编译,仍以第三种情况为例,可以编译如下代码:
情况 1 和 2 与情况 3 完全相同。
由此可以得出,上述三种情况编译失败的原因并不是函数类型调用错误,而是与“类”有关。 编译不通过的情况是“类”的成员函数用函数指针调用,非成员函数编译后用函数指针调用,且函数的类型完全一样。 那么,指向“类”的成员函数的指针和指向非成员函数的指针有什么区别吗?
在下面的程序中,使用()函数查看各个“类”的成员函数指针和非成员函数指针的长度(大小),并将其输出到屏幕上。
输出结果为(VC++6.0编译,在Win98操作系统上运行,其他操作系统可能不同):
一般非成员函数指针长度=4
-类的成员函数指针的长度-
Test3类成员函数指针长度=4
Test5类成员函数指针长度=8
Test4类成员函数指针长度=12
测试类成员函数指针长度=16
以上结果表明,在32位Win98操作系统中,一般函数指针的长度为4个字节(32位),而类的成员函数指针的长度随着类的定义、继承等的变化而变化。类的类型和关系,从无继承关系的类(Test3)的4字节(32位)到有虚继承关系的类()的12字节(96位)(Test4),只有类(Test)符合没有定义(测试),因为一些信息不清楚 成员函数指针最多 16 字节(128 位)。 显然,与一般的函数指针不同,指向“类”的成员函数的指针不仅包含成员函数地址的信息,还包含与类的属性相关的信息。 因此,通用函数指针和类成员函数指针是有本质区别的,这两种类型当然不能使用通用函数指针来直接调用类的成员函数,这也是为什么会出现开头提到的三种情况的编译错误本文。 虽然用早期版本的编译软件编译仍然会通过,但这会给程序留下严重的隐患。
至于为什么也是指向类的成员函数的指针,它的长度不一样,从32位到128位,差别很大,因为我没有看到微软官方的资料,只能推测VC+ +6。 函数指针经过优化,尽量减少指针长度,毕竟在32位操作系统上使用128位或96位指针都会对程序性能产生影响。 但是,无论如何优化,可以肯定的是,类的成员函数指针中包含一定量的()信息。 读者可以利用上述程序来验证其他操作系统和编译软件是否也进行了类似的处理。
那么,在需要的时候如何用指针调用类的成员函数呢? 可以考虑以下方法:
(1) 设置要调用的成员函数为类型,例如:在前面提到的示例2中,在类Test2的成员函数的定义前添加以下内容(修改内容以粗体显示):
更改后的代码编译成功。 原因是类型的成员函数是与类分离的,其函数指针不包含对象信息,这与一般函数指针一致。 这种方法虽然简单,但是有两个缺点: 1、任何类的成员(包括变量和函数)都不能出现在被调用函数成员的定义中; 2.由于使用了成员,类在继承时受到限制。
(2) 使用参数包含对象信息的类型的成员函数来间接调用其他成员函数。 以例3为例,对类Test3进行如下修改(黑体字为修改),main()函数不做改动,编译即可成功通过:
这种间接方法对成员函数没有限制,克服了第一种方法的成员函数不能使用任何类的成员的缺点,但由于成员的存在,类的继承仍然受到限制。
(3) 使用全局函数( )来调用间接调用类的成员函数。 仍以例3为例,修改代码如下(VC++6.0编译通过):
该方法不需要任何成员函数,但需要更多代码。
除了上述三种方法外,还有其他方法,例如修改汇编级别的代码来解决上述问题等,这些不属于本文讨论的范围。
结论:函数指针不能直接调用类的成员函数,需要间接的方法。 原因在于成员函数指针与一般函数指针有着本质的不同。 成员函数指针除了携带地址信息外,还携带着它所属的对象信息。 本文提供了三种间接调用成员函数的方法。 这三种方法各有优缺点,适合不同的场合。
希望通过以上内容的介绍,能够给您带来帮助。