深度剖析 函数指针

声明:以下代码全部在windows7  vs2010环境下编译通过,并执行无误。

全局函数指针

是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址。

函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数。

函数指针的声明方法:

数据类型标志符指针变量名(形参列表);

一般函数的声明为:

intfunc ( int x);

而一个函数指针的声明方法为:

int (*func) (intx);

前面的那个(*func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针的函数,后面的形参要视这个函数指针所指向的函数形参而定。

然而这样声明我们有时觉得非常繁琐,于是typedef可以派上用场了,我们也可以这样声明:

typedefint (*PF)(int x);

PF pf;

这样pf便是一个函数指针,方便了许多。当要使用函数指针来调用函数时,func(x)或者  (*fucn)(x) 就可以了,当然,函数指针也可以指向被重载的函数,编译器会为我们区分这些重载的函数从而使函数指针指向正确的函数。

函数指针的调用方法:

(*pf)(x);

pf(x);

int fun (inti)
{
	cout<<"call fun\n";
	returni;
}
int _tmain(intargc, _TCHAR* argv[])
{
	typedefint (*pFun)(int);//函数指针类型定义;
	pFun pf1 = fun;//指针赋值 & 符号在这里可有可无;
	pFun pf2 = &fun;
	pf1(5);//函数调用 * 符号可有可无
	(*pf2)(6);
	pFunpfarr[] = {fun,&fun};//函数指针数组;
	pfarr[0](7);//函数指针数组调用;
	(*pfarr[1])(8);
	int (*pArr[5])(int);//函数指针数组的定义
	pArr[0] = fun;
	fun(9);
return 0;
}

全局函数指针用法简单注释已经做了说明就不多说了。

指向成员函数的指针

是指向成员函数的指针变量,在C编译时,每一个成员函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址。相比较于指向函数的指针,在声明、定义时多了函数的作用域,在调用时需要有个一类对象的实体或者指针进行调用。

好了废话不多说了,切入主题。

函数指针的声明方法:

数据类型标志符函数作用域::指针变量名(形参列表);

而一个函数指针的声明方法为:

int (CLASSNAME::*func) (int x);

跟全局函数指针类似。

然而这样声明我们有时觉得非常繁琐,于是typedef可以派上用场了,我们也可以这样声明:

typedefint (CLASSNAME::*PF)(int x);

PF pf;

也跟全局指针类似。

也可以声明类成员变量的指针:

int(CLASSNAME::*pi) = CLASSNAME::m_i;这个不是重点就不做详细介绍了。

函数指针的调用方法:

(object.*pf)(x);

(objectpoint->*pf)(x);

举例:

CASE 1

如何声明、调用指向成员函数的指针

classfuncptr
{
public:
	void show(inti)
	{
		cout<<"call funcptr::show"<<i<<endl;
	}
};
int _tmain(intargc, _TCHAR* argv[])
{
	typedefvoid (funcptr::*pFun)(int);
	funcptrfptr;
	funcptr *pfptr = newfuncptr();
	pFun pf1 = &funcptr::show;//必须用 & 取函数地址;
	//pFun pf2 = funcptr::show;//编译错误;请使用“&funcptr::show”创建指向成员的指针;
	//(*pf1)(5); //编译错误
	(fptr.*pf1)(6);
	(pfptr->*pf1)(7);
deletepfptr;
}

CASE 2

如果函数指针定义在类内部,作为一个成员变量又该如何调用呢?请看我下面的例子。

classfuncptr
{
public:
	typedefvoid (funcptr::*pFun)(int);
	pFunpf;
public:
	funcptr()
	{
		pf = &funcptr::show;
	}
	void show(inti)
	{
		cout<<"call funcptr::show"<<i<<endl;
	}
	voidcallfuncptr (inti)
	{
		return (this->*pf)(i);
	}
};
int _tmain(intargc, _TCHAR* argv[])
{
	funcptrfuncp;
	//*(funcp.pf)(8);//编译错误,相当于(*pf1)(5);;
	(funcp.*(funcp.pf))(9);//借用类实体才能实现;
	funcp.callfuncptr(10);
}

(funcp.*(funcp.pf))(9);//借用类实体才能实现;

想要直接用对象的成员变量调用函数指针就是如此的麻烦,所以推荐大家在类中定义一个专门用来调用函数指针的函数来调用函数指针。

voidcallfuncptr (inti)
	{
		(this->*pf)(i);
	}

CASE 3

如果在B 类中定义了 A 类的成员函数指针,又该如何做呢?

Class funcptr
{
public:
	typedefvoid (funcptr::*pFun)(int);
	pFun pf;
public:
	funcptr()
	{
		pf = &funcptr::show;
	}
	void show(inti)
	{
		cout<<"call funcptr::show"<<i<<endl;
	}
	void show2(inti)
	{
		cout<<"call funcptr::show2"<<i<<endl;
	}
	voidcallfuncptr (inti)
	{
		return (this->*pf)(i);
	}
};

class other
{
private:
	funcptr m_funcp;
public:
	typedefvoid (funcptr::*pFun)(int);
	pFunpf;
public:
	other(){}
	other(inti)
	{
		if (1 == i)
		{
			pf = &funcptr::show;
		}
		else
		{
			pf = &funcptr::show2;
		}

	}
	void callfunptr(inti,funcptr&funcp) //在这里需要外部传入一个对象实习抑或是一个指针都可以,这一点很重要。
	{
		return (funcp.*(this->pf))(i);
	}
};
int _tmain(intargc, _TCHAR* argv[])
{
	funcptr funcp;
	//*(funcp.pf)(8);//编译错误,相当于(*pf1)(5);;
	(funcp.*(funcp.pf))(9);//借用类实体才能实现;
	funcp.callfuncptr(10);
	other oth1(1);//定义两个不同的对象实体让同一个函数指针指向不用的函数;
	other oth2(2);
	oth1.callfunptr(11,der);//传入需要的funcptr实体类;
	oth2.callfunptr(12,der);
}

CASE 4

如果类出现了继承和派生又要如何处理呢?c++中的继承本身就是一个十分复杂的东西,各个不同的编译器为我们做了不同的内部处理总是让程序员摸不着头脑。明知山有虎偏向虎山行,那我们就来试试看出现了简单的单继承继承又该怎么办呢?

CASE 4.1

         简单继承不做扩展和改写

classderive:publicfuncptr
{
	//子类中什么都不写,全部直接用父类;
};

int _tmain(intargc, _TCHAR* argv[])
{
	funcptrfuncp;
	derive der; //在这里仅仅是用派生类对象替换了父类对象,显然是满足 “is-a”这种关系;
	//*(funcp.pf)(8);//编译错误,相当于(*pf1)(5);;
	(der.*(der.pf))(9);//借用类实体才能实现;
	der.callfuncptr(10);
	other oth1(1);//定义两个不同的对象实体让同一个函数指针指向不同的函数;
	other oth2(2);
	oth1.callfunptr(11,der);//传入需要的funcptr实体类;
	oth2.callfunptr(12,der);
}

derive der; //在这里仅仅是用派生类对象替换了父类对象,显然是满足 “is-a”这种关系;

这里用派生类和基类没有任何区别;

CASE 4.2

派生类覆盖、改写基类中的函数

classfuncptr
{
public:
	typedefvoid (funcptr::*pFun)(int);
	pFun pf;
public:
	funcptr()
	{
		pf = &funcptr::show;
	}
	void show(inti)
	{
		cout<<"call funcptr::show"<<i<<endl;
	}
	Virtual void show2(inti)//这里写成虚函数;
	{
		cout<<"call funcptr::show2"<<i<<endl;
	}
	voidcallfuncptr (inti)
	{
		return (this->*pf)(i);
	}
};
Class derive:publicfuncptr
{
public:
	derive()
	{

	}
	void show (inti)
	{
		cout<<"call derive::show"<<i<<endl;
	}
	virtualvoid show2(inti)
	{
		cout<<"call derive::show2"<<i<<endl;
	}
};
int _tmain(intargc, _TCHAR* argv[])
{
	derive der; //在这里仅仅是用派生类对象替换了父类对象,显然是满足 “is-a”这种关系;
	der.callfuncptr(10);
}

当派生类调用其继承的函数指针时,并没有理会派生类覆盖掉子类的 show; 函数指针任然指向基类的函数地址,调用了基类的 show()函数;

但是如果将

pf = &funcptr::show;改写为

pf = &funcptr::show2;//注意show2为一个虚函数。

那么函数指针指向的将是派生类的函数地址,调用的是派生类的改写的函数。基类经过派生后,若派生类改写了基类的虚函数,那么基类的虚函数列表中相应的虚函数地址也修改为派生的虚函数地址,这样才能实现C++的多态性。《深度探索C++对象模型》给出了很详细的解释,这里就不再做说明。

CASE 4.3

函数指针定义在其他的非派生类中

class other
{
private:
	funcptrm_funcp;
public:
	typedefvoid (funcptr::*pFun)(int); //在这里已经将pFun的作用域设定为 funcptr::;
	pFunpf;
public:
	other(){}
	other(inti)
	{
		if (1 == i)
		{
			pf = &funcptr::show;//在给函数指针赋值时已经确定函数的作用域;
		}
		else
		{
			pf = &funcptr::show2;
		}
	}
	voidcallfunptr(inti,funcptr&funcp) //在这里需要外部传入一个对象实习抑或是一个指针都可以,这一点很重要。
	{
		return (funcp.*(this->pf))(i);//这里与借用的对象实体没有关系;
	}
};
int _tmain(intargc, _TCHAR* argv[])
{
	funcptrfuncp;
	derive der; //在这里仅仅是用派生类对象替换了父类对象,显然是满足 “is-a”这种关系;
	//*(funcp.pf)(8);//编译错误,相当于(*pf1)(5);;
	//(der.*(der.pf))(9);//借用类实体才能实现;
	//der.callfuncptr(10);
	other oth1(1);//定义两个不同的对象实体让同一个函数指针指向不同的函数;
	other oth2(2);
	oth1.callfunptr(11,funcp);//传入需要的funcptr实体类;
	oth1.callfunptr(12,der);
	oth1.callfunptr(13,funcp);
	oth1.callfunptr(14,der);
}

若在外部定义函数指针,在定义之初就已经确定了指向函数的作用域,与借用的对象实体没有关系;

class other
{
private:
	funcptrm_funcp;
public:
	typedefvoid (derive::*pFun)(int); //在这里已经将pFun的作用域设定为 funcptr::;
	pFunpf;
public:
	other(){}
	other(inti)
	{
		if (1 == i)
		{
			pf = &derive::show;//在给函数指针赋值时已经确定函数的作用域;
		}
		else
		{
			pf = &derive::show2;
		}
	}
	voidcallfunptr(inti,derive* funcp) //在这里需要外部传入一个对象实习抑或是一个指针都可以,这一点很重要。
	{
		return (funcp->*(this->pf))(i);//这里与借用的对象实体没有关系;
	}
};
int _tmain(intargc, _TCHAR* argv[])
{
	funcptrfuncp;
	derive der; //在这里仅仅是用派生类对象替换了父类对象,显然是满足 “is-a”这种关系;
	//*(funcp.pf)(8);//编译错误,相当于(*pf1)(5);;
	//(der.*(der.pf))(9);//借用类实体才能实现;
	//der.callfuncptr(10);
	other oth1(1);//定义两个不同的对象实体让同一个函数指针指向不同的函数;
	other oth2(2);
	oth1.callfunptr(11,dynamic_cast<derive*>(&funcp));//传入需要的funcptr实体类;
	oth1.callfunptr(12,&der);
	oth1.callfunptr(13,dynamic_cast<derive*>(&funcp));
	oth1.callfunptr(14,&der);
}

总结:

以上的分析已经很深入了,如果读者能阅读到这里,那么相信也已经对函数指针有了充分的了解。

综上所诉:

总的来说就一句话,函数指针就是函数入口地址,
下面再根据不同种类指针分别总结.

全局的函数指针保存了函数的入口地址,这个是毋庸置疑的。

成员函数的函数指针

1.        非虚函数即该函数的入口地址;

2.        虚函数即该对象的虚函数列表中相应虚函数的地址值,有可能派生类已经改写过了这个虚函数。亦可理解为记录了虚函数在虚函数列表中的便宜量,而不是记录虚函数的地址值。

如有说明不正确的地方肯请指正。

时间: 2024-08-08 13:53:21

深度剖析 函数指针的相关文章

深度剖析智能指针

RALL:资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数中完成资源的分配和初始化,在析构函数中完成资源的清理. 首先来看这样一个例子: 此例子乍一看上去,new/delete匹配,并没有什么错.但就因为return的存在,使得Test()函数提前结束,并没有执行delete p.这就使得内存泄露. 内存泄露的危害:使得可用内存越来越少,下一次开辟可能不够,程序崩溃. 为解决此类问题,引入智能指针. 所谓的智能指针就是智能/自动化的管理指针所指向的动态资源的释放. 智能指针的行为类

各种字符串函数(strcpy,strcmp,strlen)之深度剖析

//字符串复制函数1 void strcpy1(char str1[], char str2[]){ int i = 0; for (; str2[i] != '\0'; i++){ str1[i] = str2[i]; } str1[i] = '\0'; } //2 void strcpy2(char str1[], char str2[]){ int i = 0; while (str1[i] = str2[i])i++; } //字符串长度函数1 int strlen1(char str[

指针与多维数组深度剖析

源码 [[email protected] ch10]# cat zippo11.c /* zippo1.c --  zippo info */ #include <stdio.h> int main(void) { int b[3]={100,200,300}; int *p;p=b; printf("b=%p,*b=%d,p=%p,*p=%d,p+1=%p,*p+1=%d \n",b,*b,p,*p,p+1,*p+1); printf("-----------

深入浅出剖析C语言函数指针与回调函数(二)

上一篇博文的地址: http://blog.csdn.net/morixinguan/article/details/65494239 这节,我们来看看函数指针与回调函数在Linux内核中的应用. 从上节我们了解到,函数指针和回调函数在开发者和用户之间的一个例子,那么这节,我将引用Linux内核中文件操作结构体来详细的说明. 我们首先来看到这个结构体,这段代码位于linux内核的include/linux/fs.h中,由于代码众多,我只截取几个最基本的例子: File_operations文件操

深入浅出剖析C语言函数指针与回调函数(一)【转】

本文转载自:http://blog.csdn.net/morixinguan/article/details/65494239 关于静态库和动态库的使用和制作方法. http://blog.csdn.NET/morixinguan/article/details/52451612 今天我们要搞明白的一个概念叫回调函数. 什么是回调函数? 百度的权威解释如下: 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说

[ 1011] &lt;&lt;C语言深度剖析&gt;&gt; 测试 TEST

/******************************** **  wzsts<C语言深度剖析>2016 ** **                    ** **     fun1~fun6代表6章节内容 ** **fun10~fun19代表fun1所调用函数 ** **                    ** **     世界因规则而美好     ** ** #if(1)可运行,#if(0)不运行. ** ** Data  Author   PC       ** **16

c 深度剖析 5

1,指针没有指向一块合法的区域 1指针没有初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> #include <string.h> struct aa {     char  *pa;     char c; }ssa,*ssb; void  main() { strcpy(ssa.pa,"abc"); printf("%s \n",ssa.pa); } 1 2 3 4 5 6 7 8

c 深度剖析 4

1 预处理 1#define 1.不能用 #define 定义注释,因为注释先于预处理被处理. 2 .宏定义表达式 1,注意展开后结合顺序,尽量多加括号 2,常量定义时注意是否溢出 1 #define SEX_YEAR (60*60*24*365)UL 3,注意空格 2 #pragma #pragma message参数,在编译信息窗口输出信息 #pragma code_seg 设置程序中函数代码存放的代码段 #pragma pack设置字对齐的方式, 3指针变量传递给函数 1 2 3 4 5

c 深度剖析 2

1 while 等循环语句 1 break 和 continue的去别 2 将短的循环写在外面,长的写在里面: 3 循环嵌套尽量短 2 void void *p,为空指针类型,可以指向任何的类型 若函数不接受参数,应写明 void, 3 return 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> int *func(void) { int a[2]={1,2}; return a; } void  main() { int *b; b=