共计 2825 个字符,预计需要花费 8 分钟才能阅读完成。
导读 | 函数指针是指函数存放在内存的代码区域内,它们同样有地址. 如果我们有一个 int test(int a) 的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。 |
函数存放在内存的代码区域内,它们同样有地址. 如果我们有一个 int test(int a) 的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。
data_types (*func_pointer)(data_types arg1, data_types arg2, ...,data_types argn);
例如:
int test(int a) | |
{ | |
return a; | |
} | |
int main(int argc, const char * argv[]) | |
{ | |
int (*fp)(int a); | |
fp = test; | |
cout<<fp(2)<<endl; | |
return 0; | |
} |
注意 :函数指针所指向的函数一定要保持函数的返回值类型,函数参数个数,类型一致。
实例
int test(int a) | |
{ | |
return a; | |
} | |
int main(int argc, const char * argv[]) | |
{ | |
typedef int (*fp)(int a); | |
fp f = test; | |
cout<<f(2)<<endl; | |
return 0; | |
} |
实例
int test(int a) | |
{ | |
return a-1; | |
} | |
int test2(int (*fun)(int),int b) | |
{ | |
int c = fun(10)+b; | |
return c; | |
} | |
int main(int argc, const char * argv[]) | |
{ | |
typedef int (*fp)(int a); | |
fp f = test; | |
cout<<test2(f, 1)<<endl; // 调用 test2 的时候,把 test 函数的地址作为参数传递给了 test2 | |
return 0; | |
} |
执行以上代码,输出结果为:
10
实例
void t1(){cout<<"test1"<<endl;} | |
void t2(){cout<<"test2"<<endl;} | |
void t3(){cout<<"test3"<<endl;} | |
int main(int argc, const char * argv[]) | |
{ | |
typedef void (*fp)(void); | |
fp b[] = {t1,t2,t3}; // b[] 为一个指向函数的指针数组 | |
b[0](); // 利用指向函数的指针数组进行下标操作就可以进行函数的间接调用了 | |
return 0; | |
} |
定义:类成员函数指针(member function pointer),是 C++ 语言的一类指针数据类型,用于存储一个指定类具有给定的形参列表与返回值类型的成员函数的访问信息。
基本上要注意的有两点:
- 函数指针赋值要使用 &
- 使用 .* (实例对象) 或者 ->*(实例对象指针)调用类成员函数指针所指向的函数
下面看两个例子:
A) 类成员函数指针指向类中的非静态成员函数
对于 nonstatic member function(非静态成员函数)取地址,获得该函数在内存中的实际地址
对于 virtual function(虚函数), 其地址在编译时期是未知的,所以对于 virtual member function(虚成员函数)取其地址,所能获得的只是一个索引值
实例
// 指向类成员函数的函数指针 | |
using namespace std; | |
class A | |
{ | |
public: | |
A(int aa = 0):a(aa){} | |
~A(){} | |
void setA(int aa = 1) | |
{a = aa;} | |
virtual void print() | |
{cout *ptr)(1000); | |
a.print(); | |
(a.*ptr)(10000); | |
a.print(); | |
return 0; | |
} |
执行以上代码,输出结果为:
A::set(): 0x8048a38 | |
B::print(): 0x1 | |
B::print(): 0x5 | |
A: 0 | |
A: 10 | |
A: 100 | |
A: 1000 | |
A: 10000 |
B) 类成员函数指针指向类中的静态成员函数
实例
using namespace std; | |
class A{ | |
public: | |
//p1 是一个指向非 static 成员函数的函数指针 | |
void (A::*p1)(void); | |
//p2 是一个指向 static 成员函数的函数指针 | |
void (*p2)(void); | |
A(){ | |
/* 对 | |
** 指向非 static 成员函数的指针 | |
** 和 | |
** 指向 static 成员函数的指针 | |
** 的变量的赋值方式是一样的,都是 &ClassName::memberVariable 形式 | |
** 区别在于:** 对 p1 只能用非 static 成员函数赋值 | |
** 对 p2 只能用 static 成员函数赋值 | |
** | |
** 再有,赋值时如果直接 &memberVariable,则在 VS 中报 "编译器错误 C2276" | |
** 参见:http://msdn.microsoft.com/zh-cn/library/850cstw1.aspx | |
*/ | |
p1 =&A::funa; // 函数指针赋值一定要使用 & | |
p2 =&A::funb; | |
//p1 =&A::funb;//error | |
//p2 =&A::funa;//error | |
//p1=&funa;//error, 编译器错误 C2276 | |
//p2=&funb;//error, 编译器错误 C2276 | |
} | |
void funa(void){puts("A"); | |
} | |
static void funb(void){puts("B"); | |
} | |
}; | |
int main() | |
{ | |
A a; | |
// p 是指向 A 中非 static 成员函数的函数指针 | |
void (A::*p)(void); | |
(a.*a.p1)(); // 打印 A | |
// 使用.*(实例对象) 或者 ->*(实例对象指针)调用类成员函数指针所指向的函数 | |
p = a.p1; | |
(a.*p)();// 打印 A | |
A *b = &a; | |
(b->*p)(); // 打印 A | |
/* 尽管 a.p2 本身是个非 static 变量, 但是 a.p2 是指向 static 函数的函数指针,** 所以下面这就话是错的! | |
*/ | |
// p = a.p2;//error | |
void (*pp)(void); | |
pp = &A::funb; | |
pp(); // 打印 B | |
return 0; | |
} |
类成员函数指针与普通函数指针不是一码事。前者要用 .* 与 ->* 运算符来使用,而后者可以用 * 运算符(称为 ” 解引用 ”dereference,或称 ” 间址 ”indirection)。
普通函数指针实际上保存的是函数体的开始地址,因此也称 ” 代码指针 ”,以区别于 C/C++ 最常用的数据指针。
而类成员函数指针就不仅仅是类成员函数的内存起始地址,还需要能解决因为 C++ 的多重继承、虚继承而带来的类实例地址的调整问题,所以类成员函数指针在调用的时候一定要传入类实例对象。
