一.简介
二.普通函数指针
1.函数指针的定义方式
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;
//定义一个函数指针
void (*pFunc)(int);
//声明并定义一个函数
void PrintInt(int i)
{
cout<<i<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
//将函数赋给函数指针
//不使用&符号也可以,因为函数名称本来就是函数的首地址,类似数组
pFunc = &PrintInt;
//通过函数指针来调用函数
pFunc(1);
//调用时也可以写的完整规范一些(*pFunc)(1)符合一般指针的标准
system("pause");
return 0;
}
结果:
请按任意键继续. . .
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;
//使用typedef定义函数指针,此时,pFunc就不再是函数指针本身的名称,而是这类函数指针的一个别名
typedef void (*pFunc)(int);
//声明并定义一个函数
void PrintInt(int i)
{
cout<<i<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
//定义一个函数指针,并赋予其初始值
pFunc func = PrintInt;
//通过函数指针来调用函数
func(1);
system("pause");
return 0;
}
结果:
请按任意键继续. . .
三.成员函数指针
成员函数指针是C++中最麻烦的东东之一,准确的说是非静态成员函数指针。
1.静态成员函数指针
静态成员函数指针与普通函数指针一样,我们看一个例子:
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;
//Base类
class Base
{
public:
static void Print(int num)
{
cout<<num<<endl;
}
};
//定义一个静态成员函数指针
typedef void (*pFunc)(int);
int _tmain(int argc, _TCHAR* argv[])
{
//将静态成员函数赋给函数指针
pFunc func = Base::Print;
//通过函数指针调用函数
func(1);
system("pause");
return 0;
}
结果:
1
请按任意键继续. . .
静态成员函数与普通的函数没有太多区别,虽然他们定义在类中,但是这个成员函数不会因为对象的不同而做出不同的处理,因为它没有this指针,所以我们可以将它看成普通的函数。
2.非静态成员函数
但是非静态成员函指针就麻烦得多,原因非静态成员函数有一个隐藏的参数--this指针,这个东东在不同的对象中是不一样的,所以很麻烦。我们定义非静态成员函数指针的时候就需要将对象的类型也写出来。调用的时候,也要根据相应的对象来调用这个函数。看一个例子:
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;
//Base类
class Base
{
public:
void Print(int num)
{
cout<<num<<endl;
}
};
//定义一个成员函数指针
typedef void (Base::*pFunc)(int);
int _tmain(int argc, _TCHAR* argv[])
{
Base base;
//取成员函数的地址,必须以这种形式,不能以对象的形式获取,而且必须加上&符号
pFunc func = &Base::Print;
//调用的时候写法也比较特殊,base.*func表示函数体,是一个整体,需要用()
(base.*func)(1);
system("pause");
return 0;
}
结果:
1
请按任意键继续. . .
分析一下成员函数指针的定义和使用,我们这样定义函数指针,(Base::*pFunc)(int),其实就相当于(*pFunc)(Base*, int),相当于普通的函数指针需要多一个this指针作为参数,而这个this指针在不同的对象中一定是不同的,所以成员函数指针之间是不能互相转化的,只有同类型的对象的函数才能赋给这种对象的函数指针。
在指针赋值的时候,注意一下写法,普通的函数指针在赋值的时候,可以不写&符号,但是成员函数指针赋值的时候比较严格,如果不写的话会报出这样的错误:
error C3867: “Base::Print”: 函数调用缺少参数列表;请使用“&Base::Print”创建指向成员的指针
而且在赋值的时候,不要用对象赋值,要用 类名::函数名 这种方式赋值,使用 对象名.函数名的时候会报出这样的错误:
error C2276: “&”: 绑定成员函数表达式上的非法操作
可见,对于成员函数指针的写法还是挺严苛的。
最后再分析一下使用,由于成员函数指针需要一个this指针作为参数,这个参数又不能直接给出,所以我们就只能通过对象来调用函数,在使用函数的时候,由于函数是一个整体,所以需要用(),在括号内部,我们通过*func获得函数,然后前面使用base.就将base作为this指针传递给了函数。
那么,既然这个函数是非静态成员函数,那么这个函数支不支持动态绑定呢?换句话说就是,我们声明的函数指针是基类的函数指针,子类覆写了这个函数,那么,用子类对象调用的时候,是调用基类的函数还是子类的函数呢?我们看一个例子:
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;
//Base类
class Base
{
public:
//虚函数
virtual void Print(int num)
{
cout<<"base: "<<num<<endl;
}
};
class Child : public Base
{
public:
//覆写基类的Print方法
void Print(int num) override
{
cout<<"child: "<<num<<endl;
}
};
//定义一个成员函数指针
typedef void (Base::*pFunc)(int);
int _tmain(int argc, _TCHAR* argv[])
{
Base base;
Child child;
//给函数指针赋值的时候,必须是用声明函数指针的类来赋值(此处为Base)
pFunc func = &(Base::Print);
//通过base对象调用函数指针
(base.*func)(1);
//通过child对象调用函数指针
(child.*func)(1);
//查看函数指针的大小
cout<<"sizeof Member Function: "<<sizeof(func)<<endl;
system("pause");
return 0;
}
结果:
base: 1
child: 1
sizeof Member Function: 4
请按任意键继续. . .
从上面的结果,我们看出,在最后使用函数指针的时候,这个this指针的对象可以是我们声明函数指针的时候的对象的子类,并且如果我们覆写了基类的函数,是可以调用子类的函数的(注意是覆写基类的virtual函数,如果只是单纯的覆盖是没有多态效果的)。
我们分析一下原因,还是这样看待这个函数指针,把它看成普通函数指针增加了一个类对象的this指针作为参数,这个形参我们可以声明为基类的指针,我们给实参的时候,可以给基类的对象,当然也可以给子类对象,这就跟我们普通函数传递参数的情况一样,然后这个参数传递进去,如果有virtual函数,那么就可以触发多态。但是我们在给函数指针赋值的时候,却只能使用基类的函数,因为我们没有声明子类的函数。
四.函数指针的作用
1.函数指针作为函数的参数
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;
//定义pcall这种函数指针的类型
typedef int (*pcall)(int, int);
//处理函数,接受一个pcall类型的函数作为参数
void Func(pcall p, int x, int y)
{
cout<<"begin func:"<<endl;
cout<<"result is : "<<p(x, y)<<endl;
cout<<"end func"<<endl;
}
//加法函数
int AddFunc(int x, int y)
{
return x + y;
}
//减法函数
int SubFunc(int x, int y)
{
return x - y;
}
int _tmain(int argc, _TCHAR* argv[])
{
//将函数指针作为参数传递给函数
//&符号可以省略
Func(&AddFunc, 2, 1);
Func(&SubFunc, 2, 1);
system("pause");
return 0;
}
结果:
result is : 3
end func
begin func:
result is : 1
end func
请按任意键继续. . .
2.函数回调
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;
class Baker
{
private:
int m_iTime; //做蛋糕的时间
static const int m_iMaxTime = 10;//假设10分钟做完
public:
//做蛋糕,如果做好了返回true,否则返回false
bool MakeCake();
//构造函数
Baker();
};
Baker::Baker() : m_iTime(0){}
bool Baker::MakeCake()
{
//假设每次调用该函数,m_iTime+1
m_iTime += 1;
if (m_iTime == m_iMaxTime)
{
cout<<"蛋糕做好了!"<<endl;
return true;
}
return false;
}
//客户函数
void GetMyCake()
{
cout<<"我来取蛋糕啦!"<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
//创建一个蛋糕店
Baker baker;
//做蛋糕
while(1)
{
//客户如果想第一时间知道蛋糕做没做好,就必须每时每刻查看蛋糕的情况
if (baker.MakeCake())
{
GetMyCake();
break;
}
else
cout<<"蛋糕没做好"<<endl;
}
system("pause");
return 0;
}
结果:
蛋糕没做好
蛋糕没做好
蛋糕没做好
蛋糕没做好
蛋糕没做好
蛋糕没做好
蛋糕没做好
蛋糕没做好
蛋糕做好了!
我来取蛋糕啦!
请按任意键继续. .
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
using namespace std;
//声明一种回调函数
typedef void (*CallBackFunc)(void);
class Baker
{
private:
int m_iTime; //做蛋糕的时间
static const int m_iMaxTime = 10;//假设10分钟做完
CallBackFunc m_pfCallBack;//回调函数
public:
//注册:留下买蛋糕的人的联系方式
void Invoke(CallBackFunc);
//打电话通知买蛋糕的人
void Notify();
//做蛋糕,如果做好了,直接通知客户
void MakeCake();
//构造函数
Baker();
};
Baker::Baker() : m_iTime(0){}
void Baker::MakeCake()
{
while(m_iTime < m_iMaxTime)
{
//假设每次调用该函数,m_iTime+1
m_iTime += 1;
}
cout<<"蛋糕做好了!"<<endl;
Notify();
}
void Baker::Invoke(CallBackFunc pfunc)
{
//注册过程
m_pfCallBack = pfunc;
cout<<"留下了您的联系方式!"<<endl;
}
void Baker::Notify()
{
if (m_pfCallBack)
m_pfCallBack();
}
//客户函数
void GetMyCake()
{
cout<<"我来取蛋糕啦!"<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
//创建一个蛋糕店
Baker baker;
//注册:留下联系方式
baker.Invoke(GetMyCake);
//注册之后,客户就不需要实时查询了
cout<<"好了叫我就好,我去玩儿啦!"<<endl;
//做蛋糕
baker.MakeCake();
system("pause");
return 0;
}
结果:
好了叫我就好,我去玩儿啦!
蛋糕做好了!
我来取蛋糕啦!
请按任意键继续. . .
五.其他问题
函数指针和指针函数
//定义一个函数指针
typedef int (*pFunc)(void);
//定义一个指针函数
int* PointerFunc(void)
{
int a = 1;
return &a;
}
函数指针*和名字是放在一起的,而指针函数*是和返回值放在一起的。
//定义一个指针函数的函数指针
typedef int* (*pFUN)(void);
参考链接:http://blog.chinaunix.net/uid-10386087-id-2959230.html