-
类成员函数指针
- 类成员函数指针
- 外文名
- member function pointer
1
|
typedef void (CCmdTarget::*AFX_PMSG)(void); |
1
|
BOOL (CWnd::*)(WPARAM, LPARAM lParam) => void (CWnd::*)() => void (CCmdTarget::*)() |
1
2
3
4
5
6
7
8
9
10
11
|
struct X { void f(int){ }; int a; }; void (X::* pmf)(int); // 一个类成员函数指针变量pmf的定义 pmf = &X::f; // 类成员函数指针变量pmf被赋值 X ins, *p; p=&ins; (ins.*pmf)(101); // 对实例ins,调用成员函数指针变量pmf所指的函数 (p->*pmf)(102); // 对p所指的实例,调用成员函数指针变量pmf所指的函数 |
C/C++函数指针与回调函数总结(函数指针数组)
墨痕诉清风 C/C++
函数有它的地址,程序运行起来了,程序里肯定有地方要放这个函数。我们知道可以用printf %p来输出一个变量的地址,数组的地址,同样我们定义了一个函数后,用这个函数的名字做输出,就可以得到这个函数的地址。那么我们得到一个函数的地址,有什么用呢?
我们可以想一下,在程序里我们定义一个变量i,然后定义一个指针p,让指针p指向i得到i的地址,然后通过*p我们就可以对i进行赋值等操作。那么,既然函数的名字可以得到函数地址,也就是说我们应该也可以用一个指针指向一个函数,得到函数的地址后,对函数做点事情,比如通过指针来调用这个函数?我们来试一下:
程序编译通过了,但是给了一个warning,warning里说这个“函数指针的初始化是有矛盾的”。但是程序通过了编译,也就是说我们是可以用一个指针指向一个函数的,只是初始化时没有对应上类型,因为我们定义的指针p是int*类型,而函数是void()类型。找对类型还不够,因为定义一个函数指针它还有特定的格式。
一、定义函数指针
定义函数指针的格式:例如:void (*p)();
void表示返回的类型,第二个括号是参数表。这样格式的意思是我们定义了一个(*p)函数指针。之后我们就可以对它做初始化,比如让它指向f函数:
这样编译程序就没有了warning。那么定义了一个函数指针,我们让它指向了函数f,之后我们怎样调用指针里的内容?也就是调用指针所指的那个f函数?
二、调用函数指针
调用函数指针的格式:例如:(*p)();
我们来试一下这样的方式调用函数指针p所指的那个函数:
在f函数里我们让它输出一句话,这样当我们的函数指针成功调用所指的函数后,函数里的那句话就会被输出,我们就知道函数调用成功了。
成功进入了f函数。
那么我们可以用函数指针来做些什么来使程序更方便灵活?
三、回调函数
定义:把一个函数的指针作为参数传递到另一函数的参数表中,让这个函数的指针被调用它所指的那个函数时,这种行为就是回调函数。也就是说这个指针函数的调用方式不是直接调用,而是在特定的条件下由另一方调用。
我们先来看这样一个例子:
函数指针
函数指针是指向函数的指针变量。
通常我们说的指针变量是指向一个整型变、字符型或数组等变量,而函数指针是指向函数。
函数指针可以像一般函数一样,用于调用函数、传递参数。
函数指针变量的声明:
1
|
typedef int (*fun_ptr)( int , int ); // 声明一个指向同样参数、返回值的函数指针变量 |
实例
以下实例声明了函数指针变量 p,指向函数 max:
#include <stdio.h> int max(int x, int y){ return x > y ? x : y; } int main(void){ /* p 是函数指针 */ int (* p)(int, int) = & max; // &可以省略 int a, b, c, d; printf("请输入三个数字:"); scanf("%d %d %d", & a, & b, & c); /* 与直接调用函数等价,d = max(max(a, b), c) */ d = p(p(a, b), c); printf("最大的数字是: %d\n", d); return 0; } #include <stdio.h> int max(int x, int y){ return x > y ? x : y; } int main(void){ /* p 是函数指针 */ int (* p)(int, int) = & max; // &可以省略 int a, b, c, d; printf("请输入三个数字:"); scanf("%d %d %d", & a, & b, & c); /* 与直接调用函数等价,d = max(max(a, b), c) */ d = p(p(a, b), c); printf("最大的数字是: %d\n", d); return 0; }
编译执行,输出结果如下:
请输入三个数字:1 2 3
最大的数字是: 3
回调函数
函数指针作为某个函数的参数
函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
简单讲:回调函数是由别人的函数执行时调用你实现的函数。
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
实例
实例中 populate_array 函数定义了三个参数,其中第三个参数是函数的指针,通过该函数来设置数组的值。
实例中我们定义了回调函数 getNextRandomValue,它返回一个随机值,它作为一个函数指针传递给 populate_array 函数。
populate_array 将调用 10 次回掉函数,并将回掉函数的返回值赋值给数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <stdlib.h> #include <stdio.h> // 回调函数void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)){ for ( size_t i=0; i<arraySize; i++) array[i] = getNextValue(); } // 获取随机值 int getNextRandomValue( void ){ return rand (); } int main( void ){ int myarray[10]; populate_array(myarray, 10, getNextRandomValue); for ( int i = 0; i < 10; i++) { printf ( "%d " , myarray[i]); } printf ( "\n" ); return 0; } |
编译执行,输出结果如下:
1680728247524916226500739849436581144108930470211272101027544145785087814587779232007237709
C++基础入门教程(九):函数指针之回调
2020-11-10 09:41:27 阅读数 2766 收藏 0在Java,要实现某个时间调用某段代码,是很简单的事情,那就是使用接口。
而在C++里,有一个比较高深的方式,那就是使用函数指针。
比如Cocos2d-x的定时器(schedule)、消息订阅(NotificationCenter)都使用了函数指针来完成回调的功能。
这也是为什么我们总是能把某个函数作为参数传进去,然后在某个时刻这个函数会被调用。
一、函数的地址
要获取一个int变量的地址很简单,比如int num; 那么num的地址就是&num。
而获取函数的地址更简单,函数的名字就是函数的地址,如下代码:
void hello();
int _tmain(int argc, _TCHAR* argv[])
{
auto p = hello;
p();
return 0;
}
void hello()
{
cout << "helloworld";
}
我们定义了一个hello函数,然后直接把函数名字赋值给指针p,于是,就可以把p当成了hello函数来使用了。
这很简单吧。
二、声明函数指针
获取函数的地址很简单,但是,如何声明函数指针就变得不那么简单了。
我们总不能每次都使用auto来逃避吧?有时候我们不得不显式地声明函数指针,那么,如何声明呢?
还记得我们说过的typedef定义类型别名吗?函数指针的声明也是一样的规则,先声明一个函数,如:void hello();
然后把函数名字换成指针,如:void (*p)();
没错,就是这么简单,void (*p)(); 就是void hello(); 的声明了。
立刻再来试试,这个函数:int getValue(float dt);
它的函数指针声明是什么?没错,就是:int (*p) getValue(float dt)
没错,就是这么简单int getValue(float dt); 就是int (*p) getValue(float dt);的函数指针声明了。
立刻再来试试,这..(小若:停~!别以为我不在你就可以乱来!)
好吧,那就不继续试了,我们来看看,刚刚那段代码可以这样写:
void hello();
int _tmain(int argc, _TCHAR* argv[])
{
void (*p)();
p = hello;
p();
(*p)(); // 偷偷加了这句
return 0;
}
void hello()
{
cout << "helloworld";
}
好了,很简单,不多说了~
另外,有没有发现我偷偷又加了一句代码?
没错,用(*p)();的方式也通过能成功调用hello函数,这是为什么呢?
三、历史原因
由于p是指针,它指向的是hello函数的地址,所以,*p就代表hello函数,于是,(*p)()就等于hello(),这是正常的逻辑。
所以,其实(*p)()才是比较正常的调用方式。
然而,由于函数名本来就是指向了函数的指针,也就是说,hello其实也是指向了函数的地址。
换句话说,p和hello其实都是指针,那么,p的调用方式和hello的调用方式应该也是一样的,于是,p()就相当于hello()。
这两种方式都是正确的,其实语法这东西,就是人定的,历史上前辈对这两种方式各持所见,于是就容忍了这两种看似冲突的方式同时存在了。
不过,我想,大部分人都会更喜欢直接用p(),而不是(*p)()吧。
四、typedef挽救复杂的函数指针
如下代码:
string hehe1(int num, float value);
string hehe2(int num, float value);
string hehe3(int num, float value);
int _tmain(int argc, _TCHAR* argv[])
{
/* 声明函数指针数组 */
string(*p[3])(int num, float value) = {hehe1, hehe2, hehe3};
string result = p[1](1, 2);
cout << result.c_str() << endl;
return 0;
}
string hehe1(int num, float value)
{
return "haha1";
}
string hehe2(int num, float value)
{
return "haha2";
}
string hehe3(int num, float value)
{
return "haha3";
}
这段代码有三个参数和返回值都相同的函数,分别是hehe1、hehe2、hehe3
然后,我们要声明一个数组,这个数组用来存放这三个函数指针。
这里的函数还算是比较简单的,所以看起来不算复杂。
但如果这样的声明出现太多的话,未免会让人很沮丧。
于是,typedef挽救了我们,我们可以复杂的声明变成这样:
int _tmain(int argc, _TCHAR* argv[])
{
/* 用HeheFunc来代替复杂的函数声明 */
typedef string(*HeheFunc)(int num, float value);
/* 声明函数指针数组 */
HeheFunc p[3] = { hehe1, hehe2, hehe3 };
string result = p[1](1, 2);
cout << result.c_str() << endl;
return 0;
}
使用typedef代替函数声明之后,我们就能很轻松地使用它,并且会让我们的代表变得很简单,很好理解。
现在,HeheFunc就代表了一种类型,什么类型呢?就是参数为(int num, float value),返回值为string的函数类型。
五、结束
C++ 类成员函数的函数指针 模板类成员函数指针
阿飞__
于 2018-08-23 18:40:17 发布
39526
收藏 134
分类专栏: C/C++ 文章标签: 函数指针
当我们在 C++ 中直接像 C 那样使用类的成员函数指针时,通常会报错,提示你不能使用非静态的函数指针:
reference to non-static member function must be called
两个解决方法:
把非静态的成员方法改成静态的成员方法
正确的使用类成员函数指针(在下面介绍)
关于函数指针的定义和使用你还不清楚的话,可以先看这篇博客了解一下:
https://blog.csdn.net/afei__/article/details/80549202
二、语法
1. 非静态的成员方法函数指针语法(同C语言差不多):
void (*ptrStaticFun)() = &ClassName::staticFun;
2. 成员方法函数指针语法:
void (ClassName::*ptrNonStaticFun)() = &ClassName::nonStaticFun;
注意调用类中非静态成员函数的时候,使用的是 类名::函数名,而不是 实例名::函数名。
三、实例:
#include <stdio.h>
#include <iostream>
using namespace std;
class MyClass {
public:
static int FunA(int a, int b) {
cout << "call FunA" << endl;
return a + b;
}
void FunB() {
cout << "call FunB" << endl;
}
void FunC() {
cout << "call FunC" << endl;
}
int pFun1(int (*p)(int, int), int a, int b) {
return (*p)(a, b);
}
void pFun2(void (MyClass::*nonstatic)()) {
(this->*nonstatic)();
}
};
int main() {
MyClass* obj = new MyClass;
// 静态函数指针的使用
int (*pFunA)(int, int) = &MyClass::FunA;
cout << pFunA(1, 2) << endl;
// 成员函数指针的使用
void (MyClass::*pFunB)() = &MyClass::FunB;
(obj->*pFunB)();
// 通过 pFun1 只能调用静态方法
obj->pFun1(&MyClass::FunA, 1, 2);
// 通过 pFun2 就是调用成员方法
obj->pFun2(&MyClass::FunB);
obj->pFun2(&MyClass::FunC);
delete obj;
return 0;
}
阿飞__
转自
首先 函数指针是指向一组同类型的函数的指针;而类成员函数我们也可以相似的认为,它是指向同类中同一组类型的成员函数的指针,当然这里的成员函数更准确的讲应该是指非静态的成员函数。前者是直接指向函数地址的,而后者我们从字面上也可以知道 它肯定是跟类和对象有着关系的。
typedef int (*p)(int,int);//定义一个接受两个int型且返回int型变量的函数指针类型
int func(int x,int y)
{
printf("func:x=%d,y=%d/n",x,y);
return (x<y?x:y);
}
int main()
{
p fun=func;//定义函数指针并给它赋上一个函数指针
cout<<"min:"<<(*fun)(4,5)<<endl;//为什么*fun需要用()扩起来呢?因为*的运算符优先级比()低,如果不用()就成了*(fun())
return 0;
}
而“指向类成员函数的指针”却多了一个类的区别:
class A
{
public:
int func(int x,int y)
{
printf("A::func:x=%d,y=%d/n",x,y);
return (x<y?x:y);
}
};
typedef int (A::*p)(int,int);//指针名前一定要加上所属类型类名 A::的限定
int main()
{
p fun=&A::func;
A a; //因为成员函数地址的解引用必须要附驻与某个对象的地址,所以我们必须创建某个对象。
cout<<"min:"<<(a.*fun)(4,5)<<endl;
return 0;
}
只是用起来 .* 感觉怪怪滴。
接下来 我们可以再扩展一下:
#include <tchar.h>
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
int func1(int x,int y)
{
printf("A::func:x=%d,y=%d/n",x,y);
return (x<y?x:y);
}
virtual int func2(int x,int y)
{
printf("A::func:x=%d,y=%d/n",x,y);
return (x>y?x:y);
}
};
class B:public A
{
public:
virtual int func2(int x,int y)
{
printf("B::func:x=%d,y=%d/n",x,y);
return (x+y);
}
};
typedef int (A::*p)(int,int);//指针名前一定要加上所属类型类名 A::的限定
typedef int (B::*p0)(int,int);
int main()
{
A a; //因为成员函数地址的解引用必须要附驻与某个对象的地址,所以我们必须创建某个对象。
p fun=&A::func1;
cout<<(a.*fun)(4,5)<<endl;
cout<<(b.*fun)(4,5)<<endl<<endl;
fun=&A::func2;
cout<<(a.*fun)(4,5)<<endl;//请注意这里调用的是虚函数,嘿嘿 还真神奇 类成员函数指针也支持多态。
cout<<(b.*fun)(4,5)<<endl<<endl;
//fun=&B::func2; //这样式错误滴,因为不存在派生类的"指向类成员函数的指针"到基类的"指向类成员函数的指针"的隐式转换
fun=(int (A::*)(int,int))&B::func2;//应该进行强制转换
cout<<(a.*fun)(4,5)<<endl;
cout<<(b.*fun)(4,5)<<endl<<endl;
p0 fun0=&B::func2;
cout<<(a.*fun)(4,5)<<endl;
cout<<(b.*fun)(4,5)<<endl<<endl;
fun0=&A::func2; //正确,因为这里进行了隐式转换
cout<<(a.*fun)(4,5)<<endl;
cout<<(b.*fun)(4,5)<<endl<<endl;
//从上面我们不难发现 指向类成员函数的指针基类和派生类的关系和指向类对象的指针基类和派生类的关系完全相反,
//基类成员函数的布局被认为是派生类成员函数布局的一个子集
return 0;
}
接下 是有关模板类的类成员函数指针的使用
实例如下:
#include <tchar.h>
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
int func(int x,int y)
{
printf("A::func : x=%d,y=%d/n",x,y);
return (x<y?x:y);
}
};
class B
{
public:
int func(int x,int y)
{
printf("B::func : x=%d,y=%d/n",x,y);
return (x>y?x:y);
}
};
template<class T>
class C
{
public:
T c;
void Print()
{
int (T::*p)(int,int)=&T::func;
(c.*p)(4,5);
}
};
int main()
{
C<A> ca;
C<B> cb;
ca.Print();
cb.Print();
return 0;
}
从上面 可以很清晰地看到。。其实它和普通的模板没有什么区别。。只不过将限定名称该为参数名就OK啦
韦凯峰Linux编程学堂
1.2.2 指向对象成员函数的指针
对象的成员函数是一个函数,那么,我们可以定义一个函数指针,指向对象的成员函数。但是,该函数指针变量的定义,与普通的函数指针变量定义不同。
首先,我们先看看指向“普通函数”的指针变量的定义方法:
数据类型名 (* 指针变量名)(参数列表);
例如:
void (*pf)(int); //定义函数指针pf,指向的函数是带有1个int类型的参数,并且,返回类型是void
所以,它可以指向一个函数,并通过函数指针调用该函数,如下:
pf = func; //将func()函数的入口地址赋给指针变量pf,所以,pf就指向func()函数
(*pf)(); //调用func()函数
而定义一个指向“对象成员函数”的指针变量则比较复杂一些,如果模仿普通函数指针的定义,定义一个函数指针,指向student类的print()函数,如下:
void (*pf)();
然后,将对象成员函数名赋给指针变量pf:
student stud("wkf","xxxmylinux.vip",xxx6572996); //定义对象
pf = stud.print;
此时,编译错误,为什么呢?成员函数与普通函数有最根本的区别:它是类中的一个成员。编译系统要求在上面的赋值语句中,指针变量的类型必须与赋值右侧函数的类型匹配,要求以下3个方面都要匹配:
(1) 函数参数的类型和参数的个数。
(2) 函数返回值类型。
(3) 函数所属的类。
在这3点中1和2这两点都匹配了,但是,第三点不匹配。指针变量pf与类无关,而print()函数却属于student类。因此,要区别普通函数和成员函数的不同性质,不能在类外直接用成员函数名作为函数入口地址去调用成员函数。
那么,应该怎么样定义指向成员函数的指针变量呢?应该采用下面的形式:
void (student::*pf)(); //定义函数指针pf ,指向student类中共有成员的函数,其返回类型是void,而且无参数
注意:(student::*pf) 两侧的括号不能省略,因为()的优先级高于*,如果无此括号,就相当于:
void student:: *(pf()); //这是返回值为void 类型指针的函数
定义指向公用成员函数的指针变量的一般形式为:
数据类型名(类名::*指针变量名)(参数列表);
可以让它指向一个公用成员函数,只需把公用成员函数的入口地址赋给一个指向公用成员函数的指针变量即可,如:
pf = &student::print;
使指针变量指向一个公用成员函数的一般形式为:
指针变量名 = &类名::成员函数名;
这样,定义的函数指针变量,指向了一个类中的一个函数。但是,当一个类实例化为多个对象的时候,到底要怎么样使用该函数指针?函数指针是指向了哪一个对象的函数。所以,我们在调用函数指针的时候,就必须指定哪一个对象。下面的测试例子说明了该问题。
在VC++系统中,也可以不写“&”取地址符,与C语言的用法一样。但是,建议在写C++程序的时候,不应省略“&”运算符。
![](https://img2023.cnblogs.com/blog/47234/202304/47234-20230410162207717-621122933.jpg)
可以看到,我们定义pfunc函数指针。它是student类中的一个函数,这个函数的类型是:
(1) 返回值是void类型;
(2) 函数参数空;
然后,在main()函数中给pfunc函数指针赋值,让它指向student::print()函数。
注意,成员函数的入口地址的正确写法是:&类名::成员函数名,如下:
pfunc = &student::print;
而不应该写成:
pfunc = &stud::print;
因为,对象的成员函数不是存放在对象的空间中的,而是存放在对象外的空间中。如果有多个同类的对象,它们公用同一个函数代码段,类定义的成员函数,不是属于某一个对象,而是属于该类,由所有的类对象共享。因此,student类中的print()成员函数,不是属于stud对象,而是属于student类。所以,给指针变量 pfunc赋值的地址,应该是student类公用的函数代码段的入口地址。
所以,使用“类名::函数名”来指定函数,那么,我们在调用函数指针所指向的函数时,应该明确指出是“哪一个对象”,所以,在调用的时候,如下:
(对象名.*函数指针名)();
其中,对象名就指定了调用该函数指针指向的哪一个对象中的函数。例如,上面的例子,给函数指针pfunc设置的是student类的print()函数。
那么,要调用执行函数指针pfunc,需要让函数指针pfunc实例化,指向某一个具体的对象。相当于某一个student类对象调用函数指针pfunc所指向的函数。所以,调用如下:
(stud.* pfunc)();
此时,就是调用stud对象中函数指针pfunc所指向的print()函数,相当于执行stud.print()函数。
【C++】关于函数指针的定义方式
定义函数指针的三种形式
形式1:typedef std::function<返回值(形参类型)> 别名
例:
typedef std::function<void (int ,int)> myfunction1;
这种比较好理解,定义一种返回值为void,形参1为int,形参2为int,这种类型的函数指针,并为这种指针取名为myfunction1;使用的时候也像平时定义普通变量一样去使用就行;
形式2:typedef 返回值类型 (*别名)(形参类型);
例:
typedef void (*myfunction2)(int ,int );
这种也比较好理解,定义一种返回值为void,形参1为int,形参2为int,这种类型的函数指针,并为这种指针取名为myfunction2,但是要记住这是一个指针,使用的时候函数名相当于地址,也就是说需要把函数名赋给它就行了;使用的时候需要解引用。
形式3:返回值类型 (*别名)(形参类型)
例:
void (*myfunction3)(int,int);
这种更是直截了当,但是与前面不同的是,这里是定义了一个函数指针,并非一种,其他方面跟第二种定义的函数指针一样。
使用示例
#include<iostream>
#include<functional>
using namespace std;
typedef std::function<void (int ,int)> myfunction1;//定义一种函数指针
typedef void (*myfunction2)(int ,int );//定义一种函数指针
void (*myfunction3)(int,int);//定义一个函数指针
//从指针层面上理解该函数,即函数的函数名实际上是一个指针,
//该指针指向函数在内存中的首地址
void add (int a ,int b )
{
cout<<a<<"+"<<b<<"="<<a+b<<endl;
}
int main(){
myfunction1 fun1;
myfunction2 fun2;
fun1 = add;
fun2 = add;
myfunction3 = add;
cout<<"使用第一种形式调用:"<<endl;
fun1(1,2);
cout<<"使用第二种形式调用:"<<endl;
(*fun2)(1,2);
cout<<"使用第三种形式调用:"<<endl;
(*myfunction3)(1,2);
return 0;
}
南来地,北往的,上班的,下岗的,走过路过不要错过!
======================个性签名=====================
之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?
下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!
如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.