C++ 函数指针 & 类成员函数指针


一、函数指针

函数存放在内存的代码区域内,它们同样有地址.如果我们有一个 int test(int a) 的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。

1、函数指针的定义方式

data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn);

例如:

int (*fp)(int a); // 这里就定义了一个指向函数(这个函数参数仅仅为一个 int 类型,函数返回值是 int 类型)的指针 fp。

实例

int test(int a)
{
return a;
}
int main(int argc, const char * argv[])
{

int (*fp)(int a);
fp = test;
coutfp(2)endl;
return 0;
}

注意:函数指针所指向的函数一定要保持函数的返回值类型,函数参数个数,类型一致。

2、typedef 定义可以简化函数指针的定义

实例

int test(int a)
{
return a;
}

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

typedef int (*fp)(int a);
fp f = test;
coutf(2)endl;
return 0;
}

3、 函数指针同样是可以作为参数传递给函数的

实例

int test(int a)
{
return a1;
}
int test2(int (*fun)(int),int b)
{

int c = fun(10)+b;
return c;
}

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

typedef int (*fp)(int a);
fp f = test;
couttest2(f, 1)endl; // 调用 test2 的时候,把test函数的地址作为参数传递给了 test2
return 0;
}

执行以上代码,输出结果为:

10

4、利用函数指针,我们可以构成函数指针数组,更明确点的说法是构成指向函数的指针数组。

实例

void t1(){couttest1endl;}
void t2(){couttest2endl;}
void t3(){couttest3endl;}

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

typedef void (*fp)(void);
fp b[] = {t1,t2,t3}; // b[] 为一个指向函数的指针数组
b[0](); // 利用指向函数的指针数组进行下标操作就可以进行函数的间接调用了

return 0;
}

二、指向类成员函数的函数指针

定义:类成员函数指针(member function pointer),是 C++ 语言的一类指针数据类型,用于存储一个指定类具有给定的形参列表与返回值类型的成员函数的访问信息。

基本上要注意的有两点:

  • 1、函数指针赋值要使用 &
  • 2、使用 .* (实例对象)或者 ->*(实例对象指针)调用类成员函数指针所指向的函数

下面看两个例子:

A) 类成员函数指针指向类中的非静态成员函数

对于 nonstatic member function (非静态成员函数)取地址,获得该函数在内存中的实际地址

对于 virtual function(虚函数), 其地址在编译时期是未知的,所以对于 virtual member function(虚成员函数)取其地址,所能获得的只是一个索引值

实例

//指向类成员函数的函数指针
#include iostream>
#include cstdio>
using namespace std;

class A
{
public:
A(int aa = 0):a(aa){}

~A(){}

void setA(int aa = 1)
{
a = aa;
}

virtual void print()
{
cout A: a endl;
}

virtual void printa()
{
cout A1: a endl;
}
private:
int a;
};

class B:public A
{
public:
B():A(), b(0){}

B(int aa, int bb):A(aa), b(bb){}

~B(){}

virtual void print()
{
A::print();
cout B: b endl;
}

virtual void printa()
{
A::printa();
cout B: b endl;
}
private:
int b;
};

int main(void)
{
A a;
B b;
void (A::*ptr)(int) = &A::setA;
A* pa = &a;

//对于非虚函数,返回其在内存的真实地址
printf(A::set(): %pn, &A::setA);
//对于虚函数, 返回其在虚函数表的偏移位置
printf(B::print(): %pn, &A::print);
printf(B::print(): %pn, &A::printa);

a.print();

a.setA(10);

a.print();

a.setA(100);

a.print();
//对于指向类成员函数的函数指针,引用时必须传入一个类对象的this指针,所以必须由类实体调用
(pa->*ptr)(1000);

a.print();

(a.*ptr)(10000);

a.print();
return 0;
}

执行以上代码,输出结果为:

A::set(): 0x8048a38
B::print(): 0x1
B::print(): 0x5
A: 0
A: 10
A: 100
A: 1000
A: 10000

B) 类成员函数指针指向类中的静态成员函数

实例

#include iostream>
using namespace std;

class A{
public:

//p1是一个指向非static成员函数的函数指针
void (A::*p1)(void);

//p2是一个指向static成员函数的函数指针
void (*p2)(void);

A(){
/*
**指向非static成员函数的指针
**和
**指向static成员函数的指针
**的变量的赋值方式是一样的,都是&ClassName::memberVariable形式
**区别在于:
**对p1只能用非static成员函数赋值
**对p2只能用static成员函数赋值
**
**再有,赋值时如果直接&memberVariable,则在VS中报”编译器错误 C2276″
**参见:http://msdn.microsoft.com/zh-cn/library/850cstw1.aspx
*/
p1 =&A::funa; //函数指针赋值一定要使用 &
p2 =&A::funb;

//p1 =&A::funb;//error
//p2 =&A::funa;//error

//p1=&funa;//error,编译器错误 C2276
//p2=&funb;//error,编译器错误 C2276
}

void funa(void){
puts(A);
}

static void funb(void){
puts(B);
}
};

int main()
{
A a;
//p是指向A中非static成员函数的函数指针
void (A::*p)(void);

(a.*a.p1)(); //打印 A

//使用.*(实例对象)或者->*(实例对象指针)调用类成员函数指针所指向的函数
p = a.p1;
(a.*p)();//打印 A

A *b = &a;
(b->*p)(); //打印 A

/*尽管a.p2本身是个非static变量,但是a.p2是指向static函数的函数指针,
**所以下面这就话是错的!
*/
// p = a.p2;//error

void (*pp)(void);
pp = &A::funb;
pp(); //打印 B

return 0;
}


总结

类成员函数指针与普通函数指针不是一码事。前者要用 .* 与 –>* 运算符来使用,而后者可以用 * 运算符(称为”解引用”dereference,或称”间址”indirection)。

普通函数指针实际上保存的是函数体的开始地址,因此也称”代码指针”,以区别于 C/C++ 最常用的数据指针。

而类成员函数指针就不仅仅是类成员函数的内存起始地址,还需要能解决因为 C++ 的多重继承、虚继承而带来的类实例地址的调整问题,所以类成员函数指针在调用的时候一定要传入类实例对象。

原文地址:https://blog.csdn.net/crayondeng/article/details/16868351

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
  1. 免费下载或者VIP会员资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
  2. 提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。 若排除这种情况,可在对应资源底部留言,或联络我们。
  3. 找不到素材资源介绍文章里的示例图片?
    对于会员专享、整站源码、程序插件、网站模板、网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
  4. 付款后无法显示下载地址或者无法查看内容?
    如果您已经成功付款但是网站没有弹出成功提示,请联系站长提供付款信息为您处理
  5. 购买该资源后,可以退款吗?
    源码素材属于虚拟商品,具有可复制性,可传播性,一旦授予,不接受任何形式的退款、换货要求。请您在购买获取之前确认好 是您所需要的资源

评论(0)

提示:请文明发言