C++函数指针与成员函数指针(补)

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

(完)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值