C
- C语言中没有类的概念,只有普通的函数。通过函数名就可以得到函数地址
- 对于fun 和 &fun 应该这样理解:
- fun是函数的首地址,它的类型是void ()
- &fun表示一个指向函数fun这个对象的地址, 它的类型是void (*)()
- 因此fun 和 &fun 所代表的地址值是一样的,但类型不一样。
- fun是一个函数
- &fun表达式的值是一个指针!
C++
- 普通函数
C++的普通函数和C中是一样的,利用函数名就可以获得函数地址。 - 类静态函数
本类所有对象公用一个静态函数,所以是同一个地址【其实类的成员函数都只有一个,解释见后文】。和普通函数一样,有了函数名就可以获得地址。
可以用类名::函数名,也可以用对象.函数名 / 对象指针->函数名。 - 类成员函数(除了静态函数外的所有类中的函数)
有这样一个类:
如果这样输出:
那么输出结果都是 :1。明显不是函数地址值。
首先分析一下这几种成员函数在运行机制的不同
- 静态函数,是独立于对象的,是类拥有的,所以我们调用静态函数,既可以通过类调用也可以通过对象调用。无论是通过类调用还是对象调用,对应的都是同一个函数。
- 动态函数,只能通过对象来调用。因为在动态成员函数中,往往都需要访问对象的成员变量。我们知道同一类型的不同对象,它们拥有类中成员变量的不同副本,所以假如动态成员函数由类来调用,我们无法知道在函数中访问的是哪一个对象的成员变量。
要输出动态函数的地址,必须通过对象来获取。
C++调用非静态的成员函数时,采用的是一种__thiscall 的函数调用方式。采用这种调用方式,编译器在编译的时候,会在调用的函数形参表中增加一个指向调用该成员函数的指针,也就是我们经常说的this指针。调用的形式类似于Base::f1(Base* this, otherparam…),在函数体中,涉及到对象的成员变量或者其他成员函数,都会通过这个this指针来调用,从而达到在成员函数中处理调用对象所对应的数据,而不会错误处理其他对象的数据。可见,虽然我们必须通过对象来调用动态函数,但是其实我们访问的都是同一个成员函数。所以我们采用&Base::f1来获取成员函数地址是没错的,动态函数同样是跟类绑定而不是跟对象绑定的。
出错的原因是,输出操作符<<没有对void(__thiscall A:: *)()类型重载,编译器将这种类型转换为bool类型,所以输出了1;
对于静态函数,其调用方式并非__thiscall,<<有对它的重载,因此类的静态函数可以直接用cout输出函数地址。我们可以用printf输出,因为他可以接收任意类型的参数,包括__thiscall类型