1. 函数指针
- 直接使用
- 通过typedef使用, 当使用了typedef后,函数指针名就变成了一个专门的函数类型名,可以直接使用该名称定义该类型的函数指针
2. 成员函数指针
首先来看一个例子:
这里出了问题,要求加上&符号,而单纯的函数指针没有这种强制要求,来看下:
加上了&后还是有问题,显示类型不匹配,这里需要加上类作用域:
加上作用域后,成员函数指针已没有问题了,但调用的时候出现问题,来看一下正确的使用方法:
上面使用了两种,一种是成员函数指针定义在全局范围,另一种是直接将成员函数指针作为类属性。其原理是一样的,注意成员函数指针调用与调用一般成员一样需要对象。
再给出一个例子:
#include <iostream>
using namespace std;
class CAnimal;
typedef void (CAnimal::*PFNMEMFUNC)();
class CAnimal
{
public:
CAnimal()
{
m_pfnEat = (PFNMEMFUNC)&CAnimal::Eat;
m_pfnBark = (PFNMEMFUNC)&CAnimal::Bark;
}
PFNMEMFUNC m_pfnEat;
PFNMEMFUNC m_pfnBark;
void Eat()
{
cout << "CAniaml::Eat()" << endl;
}
void Bark()
{
cout << "CAniaml::Bark()" << endl;
}
};
class CCat : public CAnimal
{
public:
CCat()
{
m_pfnEat = (PFNMEMFUNC)&CCat::Eat;
m_pfnBark = (PFNMEMFUNC)&CCat::Bark;
}
void Eat()
{
cout << "CCat::Eat()" << endl;
}
void Bark()
{
cout << "CCat::Bark()" << endl;
}
};
class CMouse : public CAnimal
{
public:
CMouse()
{
m_pfnEat = (PFNMEMFUNC)&CMouse::Eat;
m_pfnBark = (PFNMEMFUNC)&CMouse::Bark;
}
void Eat()
{
cout << "CMouse::Eat()" << endl;
}
void Bark()
{
cout << "CMouse::Bark()" << endl;
}
};
int main()
{
CAnimal* pAni[] = {
new CCat,
new CMouse,
new CMouse
};
for (auto pAnimal : pAni)
{
// 调用成员函数指针对应的函数必须要经过如下3步
// 其原因在于成员函数指针需要对象才可调用
// 所以要先取出该成员然后在通过对象调用
// 1. 通过CAnimal类型的对象指针获取指向Eat()的函数指针成员
// 2. 解引用获取Eat()函数指针成员的地址
// 3. 通过pAnimal获取成员函数指针并调用该成员函数
(pAnimal->*(pAnimal->m_pfnEat))();
(pAnimal->*pAnimal->m_pfnBark)();
}
system("pause");
return(0);
}
上面的方法也可以实现多态,但是很麻烦而且随着成员函数变多,类对象也会越来越大。C++通过虚函数以及纯虚接口来帮助我们实现这个功能。当然也可以直接模仿虚表的机制,只在类内放入一个指向虚表的指针,虚表用一个全局数组或者静态成员函数指针数组来代替。但这种方式也有一个问题,就是函数指针数组的类型是固定的,如果类内有不同的函数就会无法使用该方法。不过依旧有解决方法,来看下面的例子:
#include <iostream>
using namespace std;
class CAnimal;
typedef void (CAnimal::*PFNMEMFUNC)();
typedef void (CAnimal::*PFNMEMFUNC1)(int iNum);
class CAnimal
{
public:
CAnimal()
{
// 填充虚表(这一步实际上在编译期就做好了,但是这里模拟所以在构造内完成)
__vftbl[0] = &CAnimal::Eat;
__vftbl[1] = &CAnimal::Bark;
__vftbl[2] = (PFNMEMFUNC)&CAnimal::Show;
// 在构造函数内让虚表指针指向虚表
__vfptr = __vftbl;
}
void Eat()
{
cout << "CAniaml::Eat()" << endl;
}
void Bark()
{
cout << "CAniaml::Bark()" << endl;
}
void Show(int iNum)
{
cout << "CAnimal::Show: " << iNum << endl;
}
// 虚表指针
PFNMEMFUNC* __vfptr;
// 模拟虚表, 位于全局区
static PFNMEMFUNC __vftbl[3];
};
PFNMEMFUNC CAnimal::__vftbl[3] = { &CAnimal::Eat, &CAnimal::Bark, (PFNMEMFUNC)&CAnimal::Show };
class CCat : public CAnimal
{
public:
CCat()
{
// 填充虚表(这一步实际上在编译期就做好了,但是这里模拟所以在构造内完成)
__vftbl[0] = (PFNMEMFUNC)&CCat::Eat;
__vftbl[1] = (PFNMEMFUNC)&CCat::Bark;
__vftbl[2] = (PFNMEMFUNC)&CCat::Show;
// 在构造函数内让虚表指针指向虚表
__vfptr = __vftbl;
}
void Eat()
{
cout << "CCat::Eat()" << endl;
}
void Bark()
{
cout << "CCat::Bark()" << endl;
}
void Show(int iNum)
{
cout << "CCat::Show: " << iNum << endl;
}
// 由于静态成员属于类而非对象,所以不同的类都需要定义
// 模拟虚表, 位于全局区
static PFNMEMFUNC __vftbl[3];
};
PFNMEMFUNC CCat::__vftbl[3] = { (PFNMEMFUNC)&CCat::Eat, (PFNMEMFUNC)&CCat::Bark, (PFNMEMFUNC)&CCat::Show };
class CMouse : public CAnimal
{
public:
CMouse()
{
// 填充虚表(这一步实际上在编译期就做好了,但是这里模拟所以在构造内完成)
__vftbl[0] = (PFNMEMFUNC)&CMouse::Eat;
__vftbl[1] = (PFNMEMFUNC)&CMouse::Bark;
__vftbl[2] = (PFNMEMFUNC)&CMouse::Show;
// 在构造函数内让虚表指针指向虚表
__vfptr = __vftbl;
}
void Eat()
{
cout << "CMouse::Eat()" << endl;
}
void Bark()
{
cout << "CMouse::Bark()" << endl;
}
void Show(int iNum)
{
cout << "CMouse::Show: " << iNum << endl;
}
// 由于静态成员属于类而非对象,所以不同的类都需要定义
// 模拟虚表, 位于全局区
static PFNMEMFUNC __vftbl[3];
};
PFNMEMFUNC CMouse::__vftbl[3] = { (PFNMEMFUNC)&CMouse::Eat, (PFNMEMFUNC)&CMouse::Bark, (PFNMEMFUNC)&CMouse::Show };
int main()
{
CAnimal* pAni[] = {
new CCat,
new CMouse,
new CMouse
};
for (auto pAnimal : pAni)
{
// 调用成员函数指针对应的函数必须要经过如下3步
// 其原因在于成员函数指针需要对象才可调用
// 所以要先取出该成员然后在通过对象调用
// 1. 通过CAnimal类型的对象指针获取指向Eat()的函数指针成员
// 2. 解引用获取Eat()函数指针成员的地址
// 3. 通过pAnimal获取成员函数指针并调用该成员函数
(pAnimal->*(pAnimal->__vfptr[0]))();
(pAnimal->*pAnimal->__vfptr[1])();
(pAnimal->*(PFNMEMFUNC1)(pAnimal->__vftbl[2]))(12);
}
system("pause");
return(0);
}
(完)