回 帖 发 新 帖 刷新版面

主题:[原创]【C++提高】成员函数指针


   来源:[url=http://dozb.blogchina.com]http://dozb.blogchina.com[/url]

   类对象的成员函数有时可以当作一般的 ANSI C 函数一样处理。正如可以声明指向对象数据成员的一般指针一样,也可以声明指向对象成员函数的指针,然后通过该指针间接调用该函数。请看例1:



     例1 使用成员函数指针
    
  class Example{
       long value;
       int time;
    public:
       long get_value(){return value;}
       long get_time(){return time + value;}
    };

    int main()
    {
       long (Example::*fp)() = &Example::get_value;
       Example e;
       
       //间接调用 Example::get_value()
       long v = (e.*fp)();

       //间接调用 Example::get_time()
       long t = (e.*fp)();
       
       return 0;
    }


    这种表示法有点讨厌。也许这就是一些程序员避免使用它的原因。指针 fp 用来调用类 Example 的两个不同的函数。同一指针可以调用不同的成员函数,但这些成员函数必须接收同样数目和类型的参数,返回同样的类型的值。如果成员函数需要参数,这种指针声明方法有些变化,如下面的例2:

    例2 指向带参数的成员函数指针
    
    #include <string.h>

    class Example{
       long value;
       char name[30];
    public:
       long set_value(long v)
       {
           value = v;
           return value;
       }
       long set_name(char* str)
       {
          strcpy(name,string);
          return name;
       }
    };

    int main()
    {
       long (Example::*fp)(long) = &Example::set_value;
       Example e;
       
       //间接调用 Example::set_value()
       long new_value = 5;
       long v = (e.*fp)(new_value);

       //间接调用 Example::set_name()
       char* (Example::*fp1)(char*) = &Example::set_name;
       char* url ="dozb.blogchina.com";
       char* new_name = (e.*fp1)(url);
       new_name = (e.*fp1)("dozb");

       return 0;
    }



上述代码给出了三种通过指针调用函数的方法。还有一个更大的变化:函数调用可以与对象指针连用。下面是类 Example 的例子:

    int main()
    {
        //声明一个对象
    Example example;

    //声明一个指针指向它
    Example * e = &example;

    //使用指向对象的指针
       long (Example::*fp)(long) = &Example::set_value;
       long v = (e->*fp)(219);

       return 0;
    }




不管传值还是传地址方式,调用非静态成员函数时都必须与具体的类的对象联系起来。静态成员函数有所不同,声明静态成员函数指针的 方法也有不同。不能用对象或对象的指针调用静态成员。如下面例3:

    例3 指向静态成员函数的指针

    class StaticExample
    {
    public:
        static int foo();
    static int woo()(;
    };

    int value;  //定义一个全局变量

    int StaticExample::foo()
    {
        return value;//记住静态成员函数不能访问类数据成员,因为它们没有 this 指针
    }

    int StaticExample::foo()
    {
        return 3;
    }
    
    int main()
    {
        int (*fp) = &StaticExample::foo;
    (*fp)();

         fp = &StaticExample::woo;
    (*fp)();

    }

回复列表 (共6个回复)

沙发

dobz每一篇都是经典,可惜我还没学c++不能看懂

板凳

跟C的函数指针差不多啊!我觉得好像没有多大意义.类里面的public成员函数用来当接口,如果再通过这种方式来使用,可能使程序不够清晰.(个人看法)

3 楼

oo中还这样用好象没有多大的意义哦

4 楼

呵呵,意义很大。我这几天专门写一下,在实际开发中什么时候需要用到。

5 楼

太高级的话题, 前面部分最好先给个应用的, 再深入探讨.
  我就不太明白! 呵呵! 多多指教!

6 楼

【C++提高】成员函数指针在开发中的实际应用

作者:dozb

前几天我的《成员函数指针》一文,主要介绍了类成员函数指针的使用方法,大家看了后认为意义不大,那是因为你可能还没有真正在开发中遇到需要用成员函数指针的情况。这篇文章就作为《成员函数指针》一文补充,举例说明在什么情况下使用成员函数指针来简化开发。

设想我们要实现一个命令解释器类,就是这个类包括了若干个命令,通常我们的实现方法如下:

class CmdProcess
{
public:
     CmdProcess();
    int execCmd(char *cmd,int argc,char*argv[]);
    void getCmdHelp(char *cmd,char* cmdhelp);
private:
    int cmd_login(int argc,char*argv[]);
    int cmd_logout(int argc,char*argv[]);
    int cmd_putfile(int argc,char*argv[]);
    int cmd_getfile(int argc,char*argv[]);
    int cmd_listfile(int argc,char*argv[]);
    int cmd_delfile(int argc,char*argv[]);
};

//根据传入的命令名字和参数执行命令
int CmdProcess::execCmd(char *cmd,int argc,char*argv[])
{
    if(strcmp(cmd,"login")==0)
     return   cmd_login(argc,argv);
    else  if(strcmp(cmd,"logout")==0)
      return cmd_logout(argc,argv);
    else if(strcmp(cmd,"putfile")==0)
      return cmd_putfile(argc,argv);
    else if(strcmp(cmd,"getfile")==0)
     return  cmd_getfile(argc,argv);
    else if(strcmp(cmd,"listfile")==0)
     return  cmd_listfile(argc,argv);
    else if(strcmp(cmd,"delfile")==0)
     return  cmd_delfile(argc,argv);
     return -1;
}

//根据传入的命令名字获取命令的帮助信息
void CmdProcess::getCmdHelp(char *cmd,char* cmdhelp)
{
    if(strcmp(cmd,"login")==0)
       ...
    else  if(strcmp(cmd,"logout")==0)
       ...
    else if(strcmp(cmd,"putfile")==0)
       ...
    else if(strcmp(cmd,"getfile")==0)
       ...
    else if(strcmp(cmd,"listfile")==0)
       ...
    else if(strcmp(cmd,"delfile")==0)
       ...
}

从上面可以看出,命令函数只是函数名不同,而参数和返回值相同,但是每次都要遍历所有的命令,烦不烦?更要命的是要是新增加一个命令,就需要修改所有的函数!假如你忘了修改一个地方,嘿嘿,后果是什么你也猜到了吧。

下面是用函数指针来实现相同的功能,可能看起来复杂一些,但要是遵循这个规则,使用起来还是比较简单的。

class CmdProcess
{
public:
     CmdProcess();
    int execCmd(char *cmd,int argc,char*argv[]);
    void getCmdHelp(char *cmd,char* cmdhelp);
private:
    typedef int (CmdProcess::* CMDFUN)(int argc,char*argv[]);//命令函数格式定义
    #define _CMDCOUNT 6
    struct _CMDINFO
    {
    char szCmdName[10];//命令名称
    char szCmdHelp[100];//命令帮助信息
    CMDFUN cmdFun;    //命令函数
    _CMDINFO(char* szCmdName,char* szCmdHelp,CMDFUN cmdFun)
    {
            strcpy(this->szCmdName,szCmdName);
        strcpy(this->szCmdHelp,szCmdHelp);
        this->cmdFun = cmdFun;
    };
    };
    _CMDINFO cmdInfoList[_CMDCOUNT];
    _CMDINFO * getCmdInfo(char *cmd);
private:
    int cmd_login(int argc,char*argv[]);
    int cmd_logout(int argc,char*argv[]);
    int cmd_putfile(int argc,char*argv[]);
    int cmd_getfile(int argc,char*argv[]);
    int cmd_listfile(int argc,char*argv[]);
    int cmd_delfile(int argc,char*argv[]);
};

//初始化成员函数指针的值
CmdProcess::CmdProcess()
{
    int i=0;
    cmdInfoList[i++] = _CMDINFO("login","login ....",cmd_login);
    cmdInfoList[i++] = _CMDINFO("logout","logout ....",cmd_logout);
    cmdInfoList[i++] = _CMDINFO("putfile","putfile ....",cmd_putfile);
    cmdInfoList[i++] = _CMDINFO("getfile","getfile ....",cmd_getfile);
    cmdInfoList[i++] = _CMDINFO("listfile","listfile ....",cmd_listfile);
    cmdInfoList[i++] = _CMDINFO("delfile","delfile ....",cmd_delfile);
}

//根据命令字找命令
_CMDINFO * CmdProcess::getCmdInfo(char *cmd)
{
   for(int i=0;i<_CMDCOUNT;i++)
   {
    if(strcmp(cmd,cmdInfoList[i].szCmdName)==0)
       return &cmdInfoList[i];
   }
   return NULL;
}

//根据传入的命令字和参数执行命令
int CmdProcess::execCmd(char *cmd,int argc,char*argv[])
{
   _CMDINFO * pCmdInfo = getCmdInfo(cmd);
   if(pCmdInfo)
      return (this->*(pCmdInfo->cmdFun))(argc,argv);//注意如何使用成员函数指针
   return -1;
}

//根据传入的命令字获取命令帮助信息
void CmdProcess::getCmdHelp(char *cmd,char* cmdhelp)
{
   _CMDINFO * pCmdInfo = getCmdInfo(cmd);
   if(pCmdInfo)
      strcpy(cmdhelp,pCmdInfo->szCmdHelp);

}

这种方式,当新增命令时,需要修改命令个数,和把相应命令添加到cmdInfoList就可以了。

我来回复

您尚未登录,请登录后再回复。点此登录或注册