手机站
网通分站
电信主站
密 码:
用户名:
当前位置 : 主页>程序设计>C/C++>列表

C 中的指针(二) 函数指针

来源:互联网 作者:west263.com 时间:2008-02-23
西部数码-全国虚拟主机10强!40余项虚拟主机管理功能,全国领先!双线多线虚拟主机南北访问畅通无阻!免费赠送企业邮局,.CN域名,自助建站480元起,免费试用7天,满意再付款! P4主机租用799元/月.月付免压金!

原帖及讨论:http://bbs.bc-cn.net/dispbbs.asp?BoardID=56&ID=92806

先说一下C式的函数指针。这种函数指针的应用十分广泛。
对于任何函数 void print(string s),他的指针这样定义:
void (*pfun)(string) = NULL;
pfun= &print;
或 pfun = print;两种写法没有区别。

pfun是指针变量名。能够指向任何只带一个string参数,返回void的函数。这里让他指向print()函数。
以后调用他的时候直接写
if (pfun)
pfun("Hello world");
C 编译器会通过pfun找到print函数,然后call print("Hello world");
一个简单应用是能够作菜单操作。例如在文本模式下的界面,让用户选择如下操作:
"0.print, 1.copy, 2.delete, 3. quit, 4.help"
那么能够写5个函数:
void print();
void copy();
void delete();
void quit();
void help();
然后用一个函数指针数组把他们存在一起:
void (*p[])() = {print, copy, delete, quit, help};
然后根据用户入0,1,2,3,4来直接叫函数
cin >> index;
p[index]();

在windows环境下编译这种函数指针被认为是用C/C 呼叫规则(C/C calling convention)。就是呼叫函数caller清理函数呼叫时生成的stack。另一种规则叫标准呼叫规则(standard calling convention)。由被叫函数callee清理自己的stack。二者一般情况下区别不大,但standard calling convention更合理,因为这样使函数size变小了一点。
实际上写C/C 函数指针的时候省略了 __cdecl 前缀。 应该写成void (__decel *p[])();
而标准规范用 __stdcall前缀。 也能够用宏CALLBACK,这也就是著名的回调函数了。

使用CALLBACK的另一个好处就是呼叫函数(caller)无需具体关心被叫函数(callee)是什么而直接呼叫。例如我们要写一个排序函数。能够用各种不同算法。如冒泡法。
void CALLBACK BubbleSort(int *pStart, int *pEnd);
也能够用quick sort
void CALLBACK QuickSort(int *pStart, int *pEnd);

那么呼叫方只需要定义一个指向这种格式的函数指针:
void (CALLBACK *p)(int*, int*),然后让p指向想用的函数就能够了。
这里只对int类型排序,实际上这种排序函数能够再叫一个CALLBACK函数来决定排序规则。以使算法能够应用到各种不同类型的变量连同不同的排序规则中。在各算法书上都有介绍。假如大家有兴趣,我能够写一下这个排序函数。

另一个典型的例子是MFC中Timer使用的CALLBACK函数,每当Timer Exprie的时候会去叫这个函数,根据返回值决定下一个动作。


C 中的函数指针和C的不同
class C
{
public:
bool test();
}
这里指向print的指针不是bool *p(),而是bool (C::*p)();
呼叫这个函数的时候这样写:
C c, *pc=&c;
bool (C::*p)() = &C::test;

c.*p();
或 pc->*p();

赋值那行bool (C::*p)() = &C::test;在VS2003里右边能够省去 C::,到了VS2005语法更严格了,被禁止了。这里的成员函数指针对非静态函数有效。静态函数不依赖于任何object,他的表示方法和C的相同。
对于非静态成员函数的指针的继承关系是这样的:upcast合法,downcast不合法。这样的到的指针永远是安全的。

非静态成员函数指针在实际程式中的应用很多。一个典型的例子是用来写state machine(状态机器?)。例如程式在控制一个机器人的初始化阶段。整个初始化需要三个函数:1。初始化机器人的身子,2。初始化机器的左手,3。初始化机器人的右手。这样我们在state machine中用两个成员函数指针分别指向当前的状态和下一个状态 bool (CStateMachine::*m_pCurrentState), bool (CStateMachine::*m_pNextState)。。
一开始永远叫Start()
CStateMachine::CStateMachine
{
m_pCurrentState = CStateMachine::Start;
}
然后在每一个State里面管理状态变化:
bool CStateMachine::Start()
{
.....
m_pNextState = CStateMachine::InitializeLeftHand();
}

bool CStateMachine::InitializeLeftHand()
{
....
m_pNextState = CStateMachine::InitializeRightHand();
}

bool CStateMachine::InitializeRightHand()
{
....
m_pNextState = NULL;
}

这样很清楚的标志了整个初始化的过程。当然这个过程也能够用很土的程式实现,设一个flag,然后把flag于函数一一对应。但那样作出来的程式不易懂,同时增加新状态的时候不好维护。

对于CStateMachine的核心部份能够这样控制:对于任何一步操作,假如函数返回true表示成功,执行下一步
(this->*(m_pCurrentState = m_pNextState))()。假如失败则报错,同时让用户选择重试(Retry)还是放弃(Abort)还是忽略(Ignor)。
假如Abort则结束StateMachine,
假如Retry则再次叫当前函数this->*m_pCurrentState();
假如Ignor则忽略当前错误继续下一步。this->*(m_pCurrentState = m_pNextState)();
当没有下一个状态的时候StateMachine结束。 (m_pNextState == NULL)
这是标准工业中的用法,大家不妨看一看,写成一个标准的class。这将是个很有用的练习。




文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

热点关注
IDC资讯 虚拟主机 域名注册 托管租用 vps主机 智能建站
网站运营 建站经验 策划盈利 搜索优化 网站推广 免费资源
网站联盟 联盟新闻 联盟介绍 联盟点评 网赚技巧
行业资讯 业界动态 搜索引擎 网络游戏 门户动态 电子商务 广告传媒
网络编程 Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术 Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷 Internet Explorer
网页制作 FrontPages Dreamweaver Javascript css photoshop fireworks Flash
程序设计 Java技术 C/C++ VB delphi
网络知识 网络协议 网络安全 网络管理 组网方案 Cisco技术
操作系统 Win2000 WinXP Win2003 Mac OS Linux FreeBSD
返回首页 |关于我们 | 联系我们 | 付款方式 | 创业联盟 | 价格总览 | 资讯中心 | 友情链接 | 网站地图 | 招贤纳士 | RSS