C++ 函数指针

1.定义
每一个函数都占用一段内存单元,它们有一个起始地址。指向函数入口地址的指针称为函数指针。
2.语法
指向函数的指针变量的定义形式:

数据类型 (*指针变量名)(参数表);
  • 函数指针定义形式中的数据类型是指函数的返回值的类型
  • (*指针变量名)括号一定要带,否则容易造成别的意思。
eg:
int (*p)(int a, int b);//p是一个指向函数的指针变量,所指函数的返回值类型为整形
int *p (int a, int b); //p是函数名,此函数的返回值是整形指针
  • 函数指针变量专门用来存放函数入口地址。哪个函数的地址赋给它,它就指向哪个函数。
  • 在给函数指针变量赋值时,只需给出函数名,不必给出参数
//函数
int max(int a, int b)
{
return a+b;
}

//定义函数指针变量p
int (*p)(int a, int b);

//将函数指针变量p指向函数max
p = max;
//则有p和max指向同一个函数的开头
  • 函数指针变量可以先后指向同一类型的不同函数。同一类型指函数返回值,函数参数列表完全相同。
//函数1:
int f1(int a, int b){
return a+b;
}
//函数2:
int f2(int c, int d){
return c*d;
//函数3:
int f3(int e){
return e*e;
}
//定义函数指针
int(*p1)(int a, int b);
int(*p2)(int e);
//赋值
p1 = f1;//正确
p2 = f3;//正确
p1 = f2;//正确
p1 = f3;//编译错误
}
  • 对函数的调用可以用函数名(),也可用函数指针。
//函数
int max (int a,int b)
{
	return a+b;
}
//定义函数指针
int (*p)(int a, int b);
p = max;

int main()
{
	int a = 3,b = 4;
	//函数调用方式1:
	int c = max(a,b);
	//函数调用方式2:
	int d = p(a,b);
	return 0;
}
  • 函数指针只能指向函数的入口处,而不能指向函数中间的某一条指令,不能用*(p+1)来表示函数的下一条指令
  • 函数指针变量常用的用途之一是把指针作为参数传递给其他函数
#include <iostream>
using namespace std;
#include <conio.h>
 
int max(int x, int y); //求最大数
int min(int x, int y); //求最小数
int add(int x, int y); //求和
void process(int i, int j, int (*p)(int a, int b)); //应用函数指针
 
int main()
{
	int x, y;
	cin>>x>>y;
 
	cout<<"Max is: ";
	process(x, y, max);
 
	cout<<"Min is: ";
	process(x, y, min);
 
	cout<<"Add is: ";
	process(x, y, add);
 
	getch();
	return 0;
}
 
int max(int x, int y)
{
	return x > y ? x : y;
}
 
int min(int x, int y)
{
	return x > y ? y : x;
}
 
int add(int x, int y)
{
	return x + y;
}
 
void process(int i, int j, int (*p)(int a, int b))
{
	cout<<p(i, j)<<endl;
}

输出结果:

3 6

Max is: 6

Min is: 3

Add is: 9

3.typedef定义可以简化函数指针的定义
格式:typedef 函数返回值类型 (*变量名) (类型 变量名,……,类型 变量名)
示例:typedef int (*p) (int a)则p就是我们定义的新类型,可以用p来声明同类型的函数指针变量。

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;
}

4.函数指针作为参数传递给函数

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

5.函数指针数组

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;
}

6.类成员函数的函数指针
类成员函数指针用于存储一个指定类具有给定形参列表与返回值类型的成员函数的访问信息。

  • 函数指针赋值要用&
  • 使用.*(实例对象)或者->*(实例对象指针)调用类成员函数指针所指向的函数

6.1类成员函数指向非静态成员函数
对于非静态成员函数取址,获得该函数在内存中的实际地址
对于虚函数,其地址在编译时期是未知的,所以对其取址获得的只是一个索引值

//指向类成员函数的函数指针
#include <iostream>
#include <cstdio>
using namespace std;
 
class A
{
    public:
        A(int aa = 0):a(aa){}
 
        ~A(){}
 
        void setA(int aa = 1)
        {
            a = aa;
        }
        
        virtual void print()
        {
            cout << "A: " << a << endl;
        }
 
        virtual void printa()
        {
            cout << "A1: " << a << endl;
        }
    private:
        int a;
};
 
class B:public A
{
    public:
        B():A(), b(0){}
        
        B(int aa, int bb):A(aa), b(bb){}
 
        ~B(){}
 
        virtual void print()
        {
            A::print();
            cout << "B: " << b << endl;
        }
 
        virtual void printa()
        {
            A::printa();
            cout << "B: " << b << endl;
        }
    private:
        int b;
};
 
int main(void)
{
    A a;
    B b;
    void (A::*ptr)(int) = &A::setA;
    A* pa = &a;
    
    //对于非虚函数,返回其在内存的真实地址
    printf("A::set(): %p\n", &A::setA);
    //对于虚函数, 返回其在虚函数表的偏移位置
    printf("B::print(): %p\n", &A::print);
    printf("B::print(): %p\n", &A::printa);
 
    a.print();
 
    a.setA(10);
 
    a.print();
 
    a.setA(100);
 
    a.print();
    //对于指向类成员函数的函数指针,使用时必须传入一个类对象的this指针,所以必须由类实体调用
    (pa->*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

6.2类成员函数指针指向静态成员函数

#include <iostream>
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;
}

6.3总结:

  • 类成员函数指针与普通函数指针不是一码事。前者要用 .* 与 ->* 运算符来使用,而后者可以用 * 运算符(称为"解引用或称"间址")
  • 普通函数指针实际上保存的是函数体的开始地址,因此也称"代码指针",以区别于 C/C++ 最常用的数据指针。
  • 类成员函数指针就不仅仅是类成员函数的内存起始地址,还需要能解决因为 C++ 的多重继承、虚继承而带来的类实例地址的调整问题,所以类成员函数指针在调用的时候一定要传入类实例对象。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值