指针(1)
函数存放在内存的代码区域内,它们同样有地址,我们如何能获得函数的地址呢?
如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。
定义一个指向函数的指针用如下的形式,以上面的test()为例:
int (*fp)(int a);//这里就定义了一个指向函数的指针
函数指针不能绝对不能指向不同类型,或者是带不同形参的函数,在定义函数指针的时候我们很容易犯如下的错误。
int *fp(int a);//这里是错误的,因为按照结合性和优先级来看就是先和()结合,然后变成了一个返回整形指针的函数了,而不是函数指针,这一点尤其需要注意!
下面我们来看一个具体的例子:
#include <iostream>
#include <string>
using namespace std;
int test(int a);
void main(int argc,char* argv[])
{
cout<<test<<endl;//显示函数地址
int (*fp)(int a);
fp=test;//将函数test的地址赋给函数学指针fp
cout<<fp(5)<<"|"<<(*fp)(10)<<endl;
//上面的输出fp(5),这是标准c++的写法,(*fp)(10)这是兼容c语言的标准写法,两种同意,但注意区分,避免写的程序产生移植性问题!
cin.get();
}
int test(int a)
{
return a;
}
typedef定义可以简化函数指针的定义,在定义一个的时候感觉不出来,但定义多了就知道方便了,上面的代码改写成如下的形式:
#include <iostream>
#include <string>
using namespace std;
int test(int a);
void main(int argc,char* argv[])
{
cout<<test<<endl;
typedef int (*fp)(int a);//注意,这里不是生命函数指针,而是定义一个函数指针的类型,这个类型是自己定义的,类型名为fp
fp fpi;//这里利用自己定义的类型名fp定义了一个fpi的函数指针!
fpi=test;
cout<<fpi(5)<<"|"<<(*fpi)(10)<<endl;
cin.get();
}
int test(int a)
{
return a;
}
源文档 <http://www.cnblogs.com/rocketfan/archive/2009/09/18/1569294.html>
指针(2)
最近在看代码,写代码的人很喜欢用回调函数和函数指针。一直觉得回调函数和函数指针挺神秘的,所以查了一些资料,来与大家一起来分享。
什么是回调函数
简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
为什么要使用回调函数
因为使用回调函数可以把调用者和被调用者分开,调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用。
如何使用回调函数
使用回调函数,我们需要做三件事:
- 声明
- 定义
- 设置触发条件:在你的函数种把你的回调函数名称转化为地址作为一个参数,以便于系统调用。
声明和定义时应注意,回调函数由系统调用,所以可以认为它属于windows系统,不要把它当作你的某个类的成员函数。
回调函数是一个程序员不能显示调用的函数,通过将回调函数的地址传给调用者从而实现调用。回调函数是十分必要的,在我们想通过一个统一接口实现不同的内容,这时回调函数非常合适。
函数指针的声明
对回调函数有了一个初步的了解,下面我们来说一下函数指针。因为要实现回调,必须首先定义函数指针。
void (*) ()
左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和右边圆括弧中的入口参数
为函数指针声明类型定义:
Typedef void(* pfv)()
pfv 是一个函数指针,它指向的函数没有输入参数,返回类型为voie。使用这个类型定义名称可以隐藏负责的函数指针语法。
void (*p)();
void func()
{
……
}
p = func;
p的赋值可以不同,但一定要是函数的指针,并且参数和返回类型相同。
例如:
现学现卖的一个小例子
#include <iostream>
using namespace std;
typedef void (*PF)();
void func()
{
cout << "func" << endl;
}
void caller( PF pf)
{
pf();
}
int main()
{
PF p = func;
caller(p);
system("pause");
return 0;
}
调用约定
在visual c++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示调用规范(默认为_cdecl)。调用规范影响编译器产生的给定函数名,参数传递的顺序,堆栈清理责任以及参数传递机制。
不过,在win32的程序中,我见得比较多的是CALLBACK,这个宏定义在windef.h中,
#define CALLBACK __stdcall
它约定了函数在它们返回到调用者之前,都会从堆栈中移除掉参数。
摘自:
回调函数
http://hi.baidu.com/spidermanzy/blog/item/b25b00956469c6097bf48016.html
回调函数以及钩子函数的概念
http://zq2007.blog.hexun.com/9068988_d.html
声明函数指针并实现回调
http://www.vckbase.com/document/viewdoc/?id=195
源文档 <http://www.cppblog.com/SpringSnow/archive/2008/12/07/68770.html>
指针(3)
C++指向类成员的指针的使用(详细介绍)
1.首先普通函数指针不能被赋值为成员函数的地址,即使返回类型和参数完全匹配。例如:下面是的pfi是一个普通函数指针,它没有参数,返回类型为int:
int (*pfi)();
若有两个全局函数,HeightIs()和WidthIs():
int HeightIs();
int WidthIs();
则下面的的赋值操作是合法的:
pfi = HeightIs();
pfi = WidthIs();
但如今有一个类Screen也定义了两个访问函数-height()和width(),它们也没有参数,
返回类型也为int:
inline int Screen::height() { return _height; }
inline int Screan::width() { return _width; }
但是下面的赋值是非法的,会导致编译错误产生。
pfi = &Screen::height();
为什么会出现违例?因为,成员函数有一个非成员函数不具有的属性-它的类(class)。
指向成员函数的指针必须于其赋值的函数类型匹配,不是两个方面而是三个方面:
(1)参数类型和个数(2)返回类型 (3) 它所属的类类型。
成员函数指针和普通函数指针之间的不匹配是由于这两种指针在表示上的区别。函数指针
存储函数的地址,可以被用来直接调用那个函数。成员函数指针首
先必须被绑定在一个对象或者一个指针上,才能得到被调用对象的this 指针,然后才调
用指针所指的成员函数。在下面将看到成员函数指针怎样被绑定到一个对象或指针上,
以便调用一个成员函数。
2.成员函数指针的声明:
拿下面这个类来说明:
class Screen {
public:
// 成员函数
void home() { _cursor = 0; }
void move( int, int );
char get() { return _screen[_cursor]; }
char get( int, int );
bool checkRange( int, int );
int height() { return _height; }
int width() { return _width; }
//....
public://修正
string _screen;
string::size_type _cursor;
short _height;
short _width;
};
成员函数指针的声明要求扩展的语法,它要考虑类的类型。对指向类数据成员的指针也
是这样。考虑Screen 类的成员_height 的类型。它的完整类型是”short 型的Screen 类的成
员“指向_height 的指针的完整类型是”指向short 型的Screen 类的成员的指针“这可以
写为:
short Screen:*
指向short型的Screen类的成员的指针定义如下:
short Screen::* ps_Screen;
ps_Screen 可以用_height 的地址初始化如下
short Screen::*ps_Screen = &Screen::_height;
在数据成员指针和普通指针之间的不匹配也是由于这两种指针的表示上的区别。普通指
针含有引用一个对象所需的全部信息。数据成员指针在被用来访问数据成员之前,必须先被
绑定到一个对象或指针上。
定义一个成员函数指针需要指定函数返回类型,参数表和类。例如指向Screen 成员函
数并且能够引用成员函数height()和width()的指针类型如下:
int (Screen::*) ();
这种类型指定了一个指向类Screen的成员函数的指针,它没有参数,返回值类型为int。
指向成员函数的指针可被声明,初始化及赋值如下
// 所有指向类成员的指针都可以用0 赋值
int (Screen::*pmf1)() = 0;
int (Screen::*pmf2)() = &Screen::height;
pmf1 = pmf2;
pmf2 = &Screen::width;
也可以用typedef 定义,这样语法更容易读。如:
typedef Screen& (Screen::*Action)();
Action default = &Screen::home;
Action next = &Screen::forward;
3.怎样使用指向类成员的指针
类成员的指针必须总是通过特定的对象或指向改类型的对象的指针来访问。是通过
使用两个指向成员操作符的指针(针对类对象和引用的.* ,以及针对指向类对象的指针的->*)
(操作符.*和->*的说明如下:
pm-expression :
cast-expression
pm-expression .* cast-expression
pm-expression ->* cast-expression
The binary operator .* combines its first operand, which must be an object of class type,
with its second operand, which must be a pointer-to-member type.
The binary operator ->* combines its first operand, which must be a pointer to an object
of class type, with its second operand, which must be a pointer-to-member type.
In an expression containing the .* operator, the first operand must be of the class type
of the pointer to member specified in the second operand or of a type unambiguously derived
from that class.
In an expression containing the ->* operator, the first operand must be of the type "pointer
to the class type" of the type specified in the second operand, or it must be of a type
unambiguously derived from that class.
)
如下面例子:
int (Screen::*pmfi)() = &Screen::height;
Screen& (Screen::*pmfS)( const Screen& ) = &Screen::copy;
Screen myScreen, *bufScreen;
// 直接调用成员函数
if ( myScreen.height() == bufScreen->height() )
bufScreen->copy( myScreen );
// 通过成员指针的等价调用
if ( (myScreen.*pmfi)() == (bufScreen->*pmfi)() )
(bufScreen->*pmfS)( myScreen );
类似地指向数据成员的指针可以按下列方式被访问:
typedef short Screen::*ps_Screen;
Screen myScreen, *tmpScreen = new Screen( 10, 10 );
ps_Screen pH = &Screen::_height;
ps_Screen pW = &Screen::_width;
tmpScreen->*pH = myScreen.*pH;
tmpScreen->*pW = myScreen.*pW;
4.静态类成员的指针:
在非静态类成员的指针和静态类成员的指引之间有一个区别。指向类成员的指针语法不
能被用来引用类的静态成员静态类成员。是属于该类的全局对象和函数它们的指针是普通
指针。
如:
class A{
public :
static void f();
public:
static int m_data;
};
指向m_data指针的定义如下:
int *p = &A::m_data;
//错误
int A::* p = &A::m_data;
指向f函数指针可以这样定义:它是一个普通的函数指针
void (*ptrf)() = &A::f;
源文档 <http://blog.csdn.net/dremi/archive/2007/11/16/1888635.aspx>
指针(4)
类成员指针
本文主要讨论C++中类成员的指针,这部分内容少有使用,也比较难于理解。
如果文中有错误或遗漏之处,敬请指出,谢谢!
需要使用类成员指针的情况是比较少见的,所以一般没人使用这个语言特性。下面先介绍类成员指针的使用方法,再介绍它的可能应用场合。
有时,我们需要在类外的其它地方直接获得类成员,这时我们就需要使用类成员指针。类成员指针与对象的指针不同,它不仅包括类的类型,还包括成员的类型。成员指针只应用于类的非static成员,因为static类成员不是任何对象的组成部分,只需使用普通指针即可。
声明成员指针
借用书上的一个例子,有这么一个类:
class Screen {
public:
typedef std::string::size_type index;
char get() const;
char get(index ht, index wd) const;
private:
std::string contents;
index cursor;
index height, width;
};
若要声明contents成员的指针,应有如下形式:
string Screen::*ps;
若要声明cursor、height或width成员的指针,应有如下形式:
Screen::index Screen::*pi;
从上面的使用形式可以看出,使用类成员的指针与普通指针的区别在于,需要在类成员指针前面的加上类限定符,以指明是哪个类的成员指针。同样,在初始化指针时,也只能用相应类的相应类型的成员对指针进行初始化,例如:
ps = &Screen::contents;
pi = &Screen::cursor;
上面介绍了对类数据成员的指针的使用方法,下面对函数成员的指针进行介绍。
函数成员的指针与普通函数指针相比,也多了类限定符。由于类成员函数还有可能是const,所以const也成为成员函数指针声明的一部分。也就是说,函数成员的指针必须在三个方面与它所指函数的类型相匹配:
(1)函数形参的类型和数目;
(2)返回类型及是否为const函数;
(3)所属类的类型。
例如,要定义Screen的get成员的指针,可以如下定义:
char (Screen::*pmf)() const = &Screen::get; // not 'Screen::get'!
char (Screen::*pmf2)(Screen::index, Screen::index) const;
pmf2 = &Screen::get;
这里需要注意的是:(1)这里不存在函数类型到函数指针类型的自动转换(即类成员函数前面必须加上&,然后再给相应指针赋值);(2)运算符的优先级关系(注意指针外层的括号所起的作用);(3)函数到指针的自动类型匹配(注意两个重载版本的get对指针的赋值)。
使用类成员指针
要使用类成员指针所指对象,首先应当从类对象取得成员指针,再解引用指针,所以有如下两种操作符供使用:.*和->*。这两个操作符的左操作数必须是类类型的对象或类类型的指针,右操作数是该类型的成员指针。例如:
Screen sc;
char c = (sc.*pmf)(); // 等价于调用sc.get();
Screen *pS = ≻
c = (pS->*pmf)();
下面给出成员指针使用的完整例子,以方便读者更好的理解:
#include <string>
class Screen {
friend void func(); // 声明func为类Screen的友元,否则无法使用类成员
public:
typedef std::string::size_type index;
char get() const {
return 'a';
}
char get(index ht, index wd) const {
return 'b';
}
private:
std::string contents;
index cursor;
index heigth, width;
};
void func()
{
std::string Screen:: *ps = &Screen::contents;
Screen::index Screen:: *pi = &Screen::cursor;
char (Screen:: *pmf) () const = &Screen::get; // not 'Screen::get'!
char (Screen:: *pmf2) (Screen::index, Screen::index) const;
pmf2 = &Screen::get;
Screen sc;
Screen *pS = ≻
Screen::index idx = sc.*pi;
idx = pS->*pi;
char c = (sc.*pmf)(); // 等价于调用sc.get();
c = (pS->*pmf)();
}
int main()
{
void (*pf)() = func; // 注意普通函数的指针的初始化,与成员指针比较
pf();
return 0;
}
成员指针的应用举例
当一个类有多个性质相同且类型相同的函数成员时,可以使用函数表来进行函数调用,产生用同一函数使用不同参数来达到不同操作的效果,而实际上是调用了不同的函数来实现的。下面给出这样的一个例子,方便读者有一个感性的认识:
#include <iostream>
using namespace std;
class Screen {
public:
Screen& home() {
cout << "Home" << endl;
return *this;
}
Screen& forward() {
cout << "Forward" << endl;
return *this;
}
Screen& back() {
cout << "Back" << endl;
return *this;
}
Screen& up() {
cout << "Up" << endl;
return *this;
}
Screen& down() {
cout << "down" << endl;
return *this;
}
// function table
typedef Screen& (Screen:: *Action)();
static Action Menu[];
// specify which direction to move
enum Directions {HOME, FORWARD, BACK, UP, DOWN};
Screen& move(Directions);
};
Screen::Action Screen::Menu[] = {&Screen::home, &Screen::forward, &Screen::back, &Screen::up, &Screen::down};
Screen& Screen::move(Directions dirc)
{
(this->*Menu[dirc])();
return *this;
}
int main()
{
Screen sc;
sc.move(Screen::HOME);
sc.move(Screen::UP);
return 0;
}
参考文献:
[1] C++ Primer(Edition 4)
[2] Thinking in C++(Edition 2)
[3] International Standard:ISO/IEC 14882:1998
源文档 <http://blog.chinaunix.net/u/18517/showart_272646.html>