今天第一次发一下关于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的地址,直接通过域就能查找到。
此外看到没有,还是需要有一个类对象,才能调用类成员变量的指针的解引用。
收工,吃饭,散。