成员函数指针
函数指针中的函数都是全局作用域内的函数,而成员函数指针指向的函数往往都是某个类的非静态成员函数。使用如下代码,编译器会报非法转换的错误。报这个错有俩个原因
- 首先类内的函数是在类作用域中的,而函数指针指向的函数是在全局作用域的。
- 成员函数有隐含的this指针参数而函数指针的函数却没有
class Foo {
void Fun()
{
cout << " Foo" << endl;
}
};
typedef void (*pFunc)();
pFunc ptr = &Foo::Fun; 错误
基于上面这个原因,我们需要定义成员函数指针去访问成员函数,而由于this指针的缘故所以成员函数的调用往往都伴随着相应的对象,C++ 定义了 .* 与 ->* 来通过使用成员函数指针去访问成员函数,由于.* 与 ->*的优先级比较低所以使用的时候往往需要加上大括号。
Return_Type (Class_Name::* pointer_name) (Argument_List);
class Foo {
void Fun()
{
cout << " Foo" << endl;
}
};
typedef void (*pFunc)();
typedef void(Foo::*pFooFunc)();
pFunc ptr = &Foo::Fun; 错误
pFooFunc ptr2 = &Foo::Fun; 正确
Foo obj;
Foo * p = new Foo;
(obj.*ptr2)(); //正确调用
(p->*ptr2)(); //正确调用
静态成员函数指针
由于静态成员函数的调用往往都是脱离于对象的,所以c++把它当做普通的函数来处理。如果我们使用一个成员函数指针去执行一个静态成员函数会报错。
#include <iostream>
#include <string>
using namespace std;
class Foo {
public:
static int f(string str) {
cout << "Foo::f()" << endl;
return 1;
}
};
int main(int argc, char *argv[]) {
// int (Foo::*fptr) (string) = &Foo::f; // 错误
int (*fptr) (string) = &Foo::f; // 正确
(*fptr)("str"); // 调用 Foo::f()
}
成员函数指针类型转化
非虚函数情况下
基类与派生类之间的转化
基类与派生类的成员函数指针发生隐式转换有俩个前提
- public继承
- 俩个函数指针对应函数的返回值和参数类型必须一样
在这俩个前提下,基类与派生类的函数指针可以互相转化。(不像赋值兼容规则只是单向的基类到派生类)
#include <iostream>
class Foo {
public:
int f(char *c = 0) {
std::cout << "Foo::f()" << std::endl;
return 1;
}
};
class FooDervied : public Foo {
public:
int f(char *c = 0) {
std::cout << "FooDerived::f()" << std::endl;
return 1;
}
};
int main(int argc, char *argv[]) {
typedef int (Foo::*FPTR) (char*);
typedef int (FooDervied::*FDPTR) (char*);
FPTR fptr = &Foo::f;
FDPTR fdptr = &FooDervied::f;
fdptr = static_cast<int(FooDervied::*)(char*)>(fptr); // 正确,逆赋值兼容规则
fptr = static_cast<int(Foo::*)(char *)>(fdptr); //正确 双向转化
}
不同类之间的成员函数指针的强转
不同类型的成员函数指针之间可以通过强转调用,但是调用并不会生效,执行的还是原来逻辑的函数,如下图代码所示。这是因为函数的地址在编译期就确定了,那么这个时候即使强转指针变量中保存的函数地址也是不会改变的,所以执行的还是强转之前的函数逻辑,但是this指针确是调用对象的this指针。
比如下面代码中A对象通过强转B对象的成员函数指针,想去调用A类的成员函数。但是即使传递的形参都是A类函数的参数,但是结果却还是只能调用B类的成员函数,但是this指针却用的是A的this指针。那么这样就形成了一个怪异的调用,使用A类的对象去调用了B类的成员函数并且this指针却是A对象的this指针。
#include <iostream>
class Foo {
public:
void f(char *c = 0) {
std::cout << "Foo::f()" << std::endl;
std::cout << *(int *)this << std::endl;
}
};
class Bar {
public:
Bar()
:a_(0)
{}
void b() {
std::cout << "Bar::b()" << std::endl;
}
int a_;
};
int main(int argc, char *argv[]) {
typedef void (Foo::*FPTR) (char*);
typedef void (Bar::*BPTR) ();
FPTR fptr = &Foo::f;
BPTR bptr = &Bar::b;
Bar obj;
( obj.*(BPTR) fptr )(); // 虽然调用的参数是按bar::b 的函数传的,但是却调用的是 Foo::f()
return 0;
}
output:
Foo::f()
0
虚函数情况下
基类与派生类之间的转化
从下面的代码可以看出,在加入虚函数机制下,原本 Foo::f 的调用变为了 Dervied::f 函数的调用,这主要是因为虚函数的调用是通过this指针访问虚表,在通过 Dervied对象的this指针访问虚表的时候,Foo::f函数已被Dervied::f函数重写,所以调用的时候发生了多态,导致Dervied::f 函数被调用。
#include <iostream>
class Foo {
public:
virtual int f(char *c = 0) {
std::cout << "Foo::f()" << std::endl;
return 1;
}
};
class FooDervied : public Foo {
public:
virtual int f(char *c = 0) {
std::cout << "FooDerived::f()" << std::endl;
return 1;
}
};
int main(int argc, char *argv[]) {
typedef int (Foo::*FPTR) (char*);
typedef int (FooDervied::*FDPTR) (char*);
FPTR fptr = &Foo::f;
FDPTR fdptr = &FooDervied::f;
fdptr = static_cast<int(FooDervied::*)(char*)>(fptr); // 正确,逆赋值兼容规则
Dervied obj;
(obj.*fdptr)(1); // Dervied::f 调用
(obj.*fptr)(1);//Dervied::f 调用 并且发生了隐式转化
}
不同类之间成员函数指针的强转
如下原本在非虚函数情况下强转失效输出Dervied::f() 却变成了 Bar::b() ,还记得上面的强转并不生效吗?即使强转,调用的还是强转前的成员函数。但是在俩边成员函数指针所指向的成员函数都是虚函数的情况下,却可以成功发生强转想要的结果,也就是通过Derived类的成员函数指针却调用了Base的成员函数。
造成这个的主要原因在于对虚函数来讲,其成员函数指针保存的不是函数地址只是调用虚函数在虚表中的偏移量。
#include <iostream>
class Foo_base{
int a_;
};
class Foo {
public:
virtual int f(char *c=0) {
std::cout << "Foo::f()" << std::endl;
++a_;
return 1;
}
virtual void Fun()
{
std::cout << "Foo::Fun()" << std::endl;
}
int a_;
};
class Bar {
public:
virtual void b() {
std::cout << "Bar::b()" << std::endl;
}
virtual int c() {
std::cout << "Bar::c()" << std::endl;
return 0;
}
};
class FooDerived : public Foo_base , public Foo {
public:
virtual int f(char *c = 0) {
std::cout << "FooDerived::f()" << std::endl;
return 1;
}
};
int main(int argc, char *argv[]) {
typedef void (FooDerived::*FPTR) ();
typedef void (Bar::*BPTR) ();
FPTR fptr = &FooDerived::Fun;
BPTR bptr = &Bar::b;
FooDerived * objDer = new FooDerived;
Bar obj;
(obj.*(BPTR)fptr)();
std::cout << fptr << endl;
std::cout << bptr << endl;
return 0;
}