C
- C语⾔中没有类的概念,只有普通的函数。通过函数名就可以得 到函数地址
#include <stdio.h>
#include <stdlib.h>
void fun() {
}
int main() {
printf("%p\n", &fun);
}
- 对于 fun 和 &fun 应该这样理解:
- fun 是函数的⾸地址,它的类型是 void ()
- &fun 表示⼀个指向函数 fun 这个对象的地址, 它的类型是 void (*)()
- 因此 fun 和 &fun 所代表的地址值是⼀样的,但类型不⼀ 样。
- fun 是⼀个函数
- &fun 表达式的值是⼀个指针!
C++
- 普通函数
C++
的普通函数和
C
中是⼀样的,利⽤函数名就可以获得函数地址。
- 类静态函数
本类所有对象公⽤⼀个静态函数,所以是同⼀个地址【其实类的成员函数
都只有⼀个
,解释⻅后⽂】。和普通函数⼀样,有 了函数名就可以获得地址。
可以⽤
类名::函数名
,也可以⽤
对象.函数名 / 对象指针->
函数名。
- 类成员函数(除了静态函数外的所有类中的函数)
有这样⼀个类:
class Base {
public:
Base() {
cout << "Base构造" << endl;
}
virtual ~Base() {
cout << "Base虚析构" << endl;
}
virtual void f1() {
cout << "Base::f1()" << endl;
}
void f2() {
cout << "Base::f2()" << endl;
}
virtual void f3() {
cout << "Base::f3()" << endl;
//cout << data << endl;
}
int data = 5;
static void fn() {
}
};
如果这样输出:
cout << &Base::f1 << endl; // 普通类成员函数
cout << &Base::f2 << endl; // 虚函数
// cout << &p->f1 << endl;
// cout << &p->f2 << endl;
// 这两个都会报错,对象绑定的函数只能⽤于调⽤
那么输出结果都是 :
1
。明显不是函数地址值。
⾸先分析⼀下这⼏种成员函数在运⾏机制的不同
- 静态函数,是独⽴于对象的,是类拥有的,所以我们 调⽤静态函数,既可以通过类调⽤也可以通过对象调⽤。⽆论是通过类调⽤还是对象调⽤,对应的都是同⼀ 个函数。
- 动态函数,只能通过对象来调⽤。因为在动态成员函 数中,往往都需要访问对象的成员变量。我们知道同⼀ 类型的不同对象,它们拥有类中成员变量的不同副本, 所以假如动态成员函数由类来调⽤,我们⽆法知道在函数中访问的是哪⼀个对象的成员变量。
要输出动态函数的地址,必须通过对象来获取。
C++调⽤⾮静态的成员函数时,采⽤的是⼀种
__thiscall
的函数调⽤⽅式。采⽤这种调⽤⽅式,编译器在编译的时候,会在调⽤的函数形参表中增加⼀个指向调⽤该成员函数的指针,也就是我们经常说的this
指针
。调⽤的形式类似于
Base::f1(Base* this, otherparam…),在函数体中,涉及到对象的成员变量或者其他成员函数,都会通过这个this
指针来调⽤,从⽽达到在成员函数中处理调⽤对象所对应的数据,⽽不会错误处理其他对象的数据。可⻅,虽然我们必须通过对象来调⽤动态函数,但是其实我们访问的都是同⼀个成员函数。所以我们采⽤ &Base::f1 来获取成员函数地址是没错的,动态函数同样是跟类
绑定⽽不是跟对象绑定的。
出错的原因是,
输出操作符 << 没有对
void(__thiscall A:: *)() 类型重载,编译器将这种类型转换为bool
类型
,所以输出了 1;
对于静态函数,其调⽤⽅式并⾮__thiscall
,
<<
有对它的重载, 因此类的静态函数可以直接⽤cout
输出函数地址。我们
可以⽤ printf 输出
,因为他可以接收任意类型的参数,包括
__thiscall 类型
cout << p->fn << endl; // 静态成员函数可以直接获取地址
cout << Base::fn << endl; // 静态成员函数可以直接获取地址
cout << &Base::f1 << endl; // 编译器将void(__thiscall A::*)()类型转换
为bool类型。 输出 1
printf("Base::f1()地址:%p\n", &Base::f1);
printf("Base::f2()地址:%p\n", &Base::f2);
printf("Base::fn()地址:%p\n", &Base::fn);