• 深度剖析 函数指针


    声明:下面代码所有在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
    ";
    	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.        虚函数即该对象的虚函数列表中对应虚函数的地址值,有可能派生类已经改写过了这个虚函数。亦可理解为记录了虚函数在虚函数列表中的廉价量,而不是记录虚函数的地址值。

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






  • 相关阅读:
    Jenkins操作学习 --邮箱配置及测试结果构建
    Jenkins操作学习 --初始化安装
    Jenkins操作学习 -- 配置及使用
    Jenkins登录后空白页
    Linux-(kill,wc,killall,ln,cal,date)
    Linux-(tar,gzip,df,du)
    Linux-(chgrp,chown,chmod)
    Linux-文件和目录属性
    Linux-(which,whereis,locate,find)
    Linux-(touch,cat,nl,more|less,head|tail)
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5100270.html
Copyright © 2020-2023  润新知