大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > C++中的指针(二)函数指针

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

关键词:函数指针C++指针  阅读(551) 赞(14)

[摘要]本文是对C++中的指针(二)函数指针的讲解,对学习C++编程技术有所帮助,与大家分享。

先说一下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。这将是个很有用的练习。



相关评论