1 函数指针详解

今天第一次发一下关于C++的文章,主要是最近做的项目要用到boost::asio作为网络库,而boost::asio要用到很多的boost::bind函数,而boost::bind又要用到自由函数指针和成员函数指针,所以这一串下来,我首先就是要讲讲函数指针这个东东。

一,自由函数指针

这个自由函数应该就是对应成员函数的那种不在类中的函数吧,英语叫做free function,我是硬翻的,欢迎同学们指正打脸。它的指针很容易写,分为两种方法吧:

(一)先声明函数指针类型,再定义函数指针实例。

比如,我有一个自由函数:

.h:

inline double aFreeFun(double param);

.cpp:

#include <iostream>

double aFreeFun(double param)

{

std::cout<<"aFreeFun function:"<<param *2 <<std::endl;

return param *3;

}

很简单是吧,它有一个double类型的参数,还有一个double类型的返回值。这里要注意的是,当你在类文件中写自由函数时,记得使用inline或者是static关键字,不然容易出现重复定义的现象。我在main函数中声明一个这个函数类型的函数指针:

int main(int argc, char *argv[])
{
    typedef double(*freeFunPoint)(double);
    freeFunPoint pointInstance = aFreeFun;
    std::cout<<"freeFunPoint function point:"<<pointInstance(5)<<std::endl;
}

看到了吗,函数指针类型,是使用typedef定义的,格式是

typedef 返回值(*函数指针类型名称)(参数列表);

这样;然后将自由函数的函数名赋给这样的一个函数指针的实例——pointInstance,其格式如下:

函数指针类型名称 函数指针实例 = 对应类型函数的地址;

随后这个实例,就可以像使用这个自由函数一样使用的了。

这里,函数名和数组名一样,可以当做指向这块内存的指针使用;其实真正正确的用法如下所示,但是木有必要。

int main(int argc, char *argv[])

{

typedef double(*freeFunPoint)(double);

freeFunPoint pointInstance = &aFreeFun;

std::cout<<"freeFunPoint function point:"<<(*pointInstance)(8)<<std::endl;

}

也就是对函数名取地址才得到函数地址,用的时候先解除地址再使用。

如果单单看这里,就会觉得这是什么方法啊,我难道不能直接调用这个自由函数吗,绕这么一个大圈子干什么;这个好处以后一定会讲到,它就是让函数也变成一个实例,在你的程序中传递。

(二)直接定义函数指针实例。

还是使用上面的自由函数,这次在main函数中这样写:

int main(int argc, char *argv[])
{
    double(*pointInstance3)(double) = aFreeFun;
    std::cout<<"freeFunPoint function point3:"<<pointInstance3(7)<<std::endl; 
}

不用typedef,不创建一个函数指针类型,直接上函数指针,格式为

返回值(*)(参数列表) =对应类型函数的地址;

当然也有前面的那个取地址和解除指针的版本,不写它了。

(三)C++11中使用using

上面已经说了只有两种了,这里又来一种,呵呵,现在C++11的概念这么火,好像有了C++11之后,C++就会变成一门不考虑内存管理,节约临时变量生成时间,拥有线程的语言一样。所以如果有C++11,就可以用using的方法来做,如下所示:

int main(int argc, char *argv[])
{
    using freeFunPoint2 = double (*)(double);
    freeFunPoint2 pointInstance2 = aFreeFun;
    std::cout<<"freeFunPoint function point2:"<<pointInstance2(6)<<std::endl;
}

这样写的话,函数指针的概念就很容易理解了,freeFunPoint2代表double(*)(double)这类型的函数指针。格式如下:

using 函数指针类型名称 = 返回值(*)(参数列表);

自由指针的内容就介绍到这里,学会了这个,就妥妥的能用设计模式中策略模式了!

二,成员函数指针

成员函数指针这里就比自由函数指针要麻烦一点点,主要是把握住一个概念:就是class、namesapce、struct等都是域的概念,他们后面加上::就能访问他们内部的静态公开的内容,而函数,虽然也是域,但是对于外界应该是一个封闭的系统,是无法访问到。

我先写一个类,对它的成员函数进行操作。

.h

class MemberFunPointStudy

{

public:

MemberFunPointStudy();

void useMemberFunc();

double aMemberFunc(double param);

};

其中函数aMemberFunc是我即将操作的函数,而函数useMemberFunc()中,我将写出,在同一个类中声明定义函数指针和函数指针实例的方法。

.cpp

double MemberFunPointStudy::aMemberFunc(double param)

{

std::cout<<"aMemberFunc function:"<<param *2 <<std::endl;

return param *3;

}

首先我们先说一下在同一个类中声明定义函数指针和函数指针实例的方法,同样的,也有三种方法使用函数指针。

(一)先声明函数指针类型,再定义函数指针实例。

void MemberFunPointStudy::useMemberFunc()

{

typedef double(MemberFunPointStudy::*useMemberFunc)(double);

useMemberFunc pointInstance =  aMemberFunc;

std::cout<<"memberFunPoint function point:"<<((*this).*pointInstance)(1)<<std::endl;

std::cout<<"memberFunPoint function point:"<<(this->*pointInstance)(3)<<std::endl;

//    std::cout<<"memberFunPoint function point:"<<((*this).pointInstance)(3)<<std::endl;

}

先说一下正确的用法,首先声明这个类的成员函数指针类型的方法如下:

typedef 返回值(类名::*函数指针类型名称)(参数列表);

而声明定义一个具体的函数指针实例如下:

函数指针类型名称 函数指针实例 = 对应类型函数的地址;

函数指针类型名称 函数指针实例 = &对应类型函数的地址;

我们发现声明这个函数指针类型与前面讲的自由函数那一套并没有什么太大区别,只是要加上这个类的域,而定义函数指针实例这里是一模一样的。类名这个域表示,这个函数指针类型创建的实例,只能接收这个类域中对应格式的函数指针。私以为,好像是不支持一个能够接收任何类域中对应格式的函数指针,当我如下写

typedef double(*useMemberFunc)(double);

useMemberFunc pointInstance =  aMemberFunc;

时,就会在第二行报如下错误:

error: cannot convert ‘MemberFunPointStudy::aMemberFunc‘

from type ‘double (MemberFunPointStudy::)(double)‘

to type ‘useMemberFunc {aka double (*)(double)}‘

它的意思及时说,aMemberFunc这个函数的类型是‘double (MemberFunPointStudy::)(double)‘,不能将它转换成为double (*)(double),所以这充分说明了成员函数的所在域很重要,它一旦属于一个类时,就不再是一个函数了,而是一个类的函数,它的类型必包含所在类的类型。这里红色标出的地方有个问题,我们下面马上会讲到。

不过在使用时就有很大区别了。可以看到,这里的使用都是需要将函数指针实例解引用之后,才能使用,而且还得由一个所属类对象或类指针调用,调用的格式也比较固定。为:

(类对象.*函数指针实例)(实参传入);

(类对象指针->*函数指针实例)(实参传入);

其实也能看出,和自由函数那里也差不了多少,就是前面指定了类对象,而调用时又用了上面说的解除指针的方法,再就是括号的用法。需要注意的是,解除指针在这里不是可选的,而是必选的,如果按照函数中注释部分运行下去就会报如下错误:

error: ‘class MemberFunPointStudy‘ has no member named ‘pointInstance‘

看到没有,编译器认不出来这个pointInstance是个函数指针啊,它只是傻傻的以为他是个成员变量,然后弱弱的告诉你木有这个成员变量好慌张怎么办!所以你必须用解引用的办法,此时编译器寻找指针指向内容,发现原来是aMemberFunc函数啊。随后,就可以用了。那么私以为,加不加这个*,加不加那个类域::,加不加内个类对象,加不加辣个类对象指针,也就是唯四的区别了,so easy,再也不用担心回调函数写不好了。

(二)直接定义函数指针实例。

void MemberFunPointStudy::useMemberFunc()

{

double(MemberFunPointStudy::*pointInstance2)(double) =  &aMemberFunc;

std::cout<<"memberFunPoint function point:"<<((*this).*pointInstance2)(1)<<std::endl;

std::cout<<"memberFunPoint function point:"<<(this->*pointInstance2)(3)<<std::endl;

}

我觉得无需多言,一看即会,加不加&都一样。

(三)C++11中使用using

void MemberFunPointStudy::useMemberFunc()

{

using useMemberFunc2 = double(MemberFunPointStudy::*)(double);

useMemberFunc2 pointInstance3 = aMemberFunc;

std::cout<<"memberFunPoint function point:"<<((*this).*pointInstance3)(1)<<std::endl;

std::cout<<"memberFunPoint function point:"<<(this->*pointInstance3)(3)<<std::endl;

}

这就是用using的方法,也是分分钟掌握啊,但是看到没有,我上面标红,说后面会将的部分就是double(MemberFunPointStudy::*)(double)这里,我试了,不加*根本连IDE自己的查看都通不过,更别说编译器了,所以编译器编译时提供的错误信息也很重要。我用的编译器是mingw492_32。

上面的三种方法,都是创建的本类的函数指针实例,最后,我们看看在别的类中如何创建,我就直接写到main函数中了。

(四)在其他类中定义函数指针实例

如下所示:

int main(int argc, char *argv[])

{

typedef double(MemberFunPointStudy::*useMemberFunc)(double);

useMemberFunc pointInstance =  &MemberFunPointStudy::aMemberFunc;

double d = (MemberFunPointStudy().*pointInstance)(2);

}

和前面的区别就是给类取地址,再通过::找到它的函数,我已经试过了,只有这一种写法,&号只能加在类名的前面,而函数的前面不能加&,原因其实也能想到,&MemberFunPointStudy已经是类的地址了,不需要也不能再查找aMemberFunc的地址,直接通过域就能查找到。

此外看到没有,还是需要有一个类对象,才能调用类成员变量的指针的解引用。

收工,吃饭,散。

时间: 2024-08-09 19:19:54

1 函数指针详解的相关文章

C++函数指针详解

学习c++的过程中,指针是难点,熟悉了指针之后,还有一个让人很蛋疼的难点,那就是函数指针了.本博文详细介绍一下常见的各种坑爹的函数指针. 至于指针的详细学习,推荐这篇博文C++指针详解 与数据一样,函数也有地址,函数的地址就是内存中存放函数语言代码的起始地址.函数指针就是指向这个地址.函数指针所指向的类型,就是函数本身.我们知道,指针所指向类型代表了指针所指向的内存区域的大小.所以函数指针所指向的类型,就是函数在内存中所占据内存的大小.知道了函数的起始地址和大小,所以函数指针可以很轻易的代替函数

c :函数指针详解

在研究opencv源代码的过程中,处处可见到函数指针,于是翻出来谭浩强的<C程序设计>把函数指针这一块内容再补一补! 1 定义 数据类型 (*指针变量名)(参数表); 注: 数据类型是指的函数返回值的类型; (*指针变量名)两侧的括号不能省略,表示p先与*结合,是指针变量,然后再与后面的(参数表)结合; (参数表)可以是一个参数,也可以是多个参数,不过在引用的时候要保证形参与实参一致. 2 int (*p)(int a, int b); 表示定义一个指向函数的指针变量p,它不是固定的指向哪一个

函数指针及其定义和用法,C语言函数指针详解

函数指针大家了解一下就行了,用得不多,但一定要认识它. 什么是函数指针 如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址.而且函数名表示的就是这个地址.既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针. 那么这个指针变量怎么定义呢?虽然同样是指向一个地址,但指向函数的指针变量同我们之前讲的指向变量的指针变量的定义方式是不同的.例如: int(*p)(int, int); 这个语句就定义了

指针数组,数组指针,指针函数,函数指针,二级指针详解

先看个简单的:char *p,这定义了一个指针,指针指向的数据类型是字符型,char  *(p)定义了一个指针P: char *p[4], 为指针数组,由于[]的优先级高于*,所以p先和[]结合,p[]是一个数组,暂时把p[]看成是q,也就是char *(q),定义了一个指针q,只不过q是一个数组罢了,故定义了一个数组,数组里面的数据是char *的,所以数组里面的数据为指针类型.所以char *p[4]是四个指针,这四个指针组成了一个数组,称为指针数组,既有多个指针组成的数组. char(*p

C++Study 指针详解

C++指针详解 指针的概念 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址.要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区.让我们分别说明. 先声明几个指针放着做例子: 例一: int *ptr; char *ptr; int **ptr; int (*ptr)[3]; int *(*ptr)[4]; 指针的类型 从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类

[转]C++ 智能指针详解

C++ 智能指针详解   一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见. 用智能指针便可以有效缓解这类问题,本文主要讲解参见的智能指针的用法.包括:std::auto_ptr.boost::scoped_ptr.boost::shared_ptr.boost::scoped_array.boost::shared

C++ 智能指针详解

一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见. 用智能指针便可以有效缓解这类问题,本文主要讲解参见的智能指针的用法.包括:std::auto_ptr.boost::scoped_ptr.boost::shared_ptr.boost::scoped_array.boost::shared_array.boost:

【C++】智能指针详解(一):智能指针的引入

智能指针是C++中一种利用RAII机制(后面解释),通过对象来管理指针的一种方式. 在C++中,动态开辟的内存需要我们自己去维护,在出函数作用域或程序异常退出之前,我们必须手动释放掉它,否则的话就会引起内存泄漏. 例如:我们用指针变量来创建对象时,需要手动来删除它 string * pstr = new string("hello world!"); .... delete pstr; 事实上,即使我们非常谨慎,但有时候的一些情况仍然会让我们防不胜防: //情况一:在程序某个分支内,忘

彻底搞定C语言指针详解

1.语言中变量的实质 要理解C指针,我认为一定要理解C中“变量”的存储实质, 所以我就从“变量”这个东西开始讲起吧! 先来理解理解内存空间吧!请看下图: 内存地址→ 6 7 8 9 10 11 12 13 ----------------------------------------------------------------- ... | | | | | | | |.. ------------------------------- ---------------------------