DiuR21Laonnu

函数指针与类成员函数指针的区别

2017/09/25

前言

​ 在C语言中,曾感受过函数指针的威力。C++也并未摒弃这一强大的开发特性。众所周知,在C++中允许定义类这一种新型的数据类型。那么,在类中定义的函数应当也可以被函数指针所指向并调用。
本文就函数指针与类成员函数指针之间的联系与区别展开讨论。

函数指针回顾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void foo1(int a = 1)
{
std::cout<<"foo1 test: "<<a<<std::endl;
}
int main()
{
foo1();
void (*fooP)(int) = foo1;
fooP(2);
}
//osx & g++ 编译通过
//输出结果为:
//foo1 test: 1
//foo1 test: 2

​ 以上代码样例,展示了函数指针的使用方法。接下来。我们接着讨论类成员函数。

类成员函数指针

​ 在C++中,通过基类指针或引用可指向派生类指针或引用【隐式向上转换】实现多态。我们不得不提出一个问题,也即在指针函数中,是否也会存在类似于多态的问题。
​ 接下来是一段代码,请越过它,跟随代码后的分析阅读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class Animal
{
public:
int value;
void speak()
{
cout << "I am a Animal!" << endl;
printf ("%d\n", &Animal::speak);
}
virtual void eat()
{
cout << "Animal eat xxx" << endl;
}
Animal()
{
value = 1;
}
};
class Cat: public Animal
{
public:
void speak()
{
cout << "I am a Cat" << endl;
}
virtual void eat()
{
cout << "Cat eat fish" << endl;
}
Cat()
{
value = 2;
}
};
typedef void (Animal::*aP)();
typedef void (Cat::*cP)();
int main()
{
Animal ae;
aP iap;
iap = &Animal::speak;
(ae.*iap)();
Cat ce;
cP icp;
icp = (void (Cat::*)())iap;
(ce.*icp)();
//------------------------------------
iap = &Animal::eat;
(ae.*iap)();
icp = &Cat::eat;
(ce.*icp)();
icp = (void (Cat::*)())iap;
(ce.*icp)();
return 0;
}

​ 在代码中,我们定义了两个类Cat & Animal。并定义Animail类中的eat函数为虚且在Cat类中重新定义了eat函数。

1
2
typedef void (Animal::*aP)();
typedef void (Cat::*cP)();

​ 定义新数据类型Animal函数指针与Cat函数指针分别为aP与cP

1
2
3
4
5
6
7
Animal ae;
aP iap;
iap = &Animal::speak;
(ae.*iap)();
//-------here are the results
//I am a Animal!
//36290096

​ 将Animail中的speak函数地址绑定到iap函数指针中,并调用。

1
2
3
4
5
6
7
Cat ce;
cP icp;
icp = (void (Cat::*)())iap;
(ce.*icp)();
//-------here are the results
//I am a Animal!
//36290096

​ 将iap【Animail函数指针】强制转换为Cat函数指针并赋值于icp,调用icp。

​ 经过观察可知,两个输出的结果相同。其原因为:对于动态成员函数,函数地址在编译之前便确定了。

ps:关于该问题,请参阅C++类中的函数地址问题

1
2
3
4
5
6
7
8
9
10
11
12
iap = &Animal::eat;
(ae.*iap)();
icp = &Cat::eat;
(ce.*icp)();
icp = (void (Cat::*)())iap;
(ce.*icp)();
//------here are the results
//Animal eat xxx
//Cat eat fish
//Cat eat fish

​ 首先强调eat函数为虚函数,虚函数常被用于多态。

​ 代码分别绑定iap & icp指针于Animal & Cat eat函数上。输出结果符合预期。

​ 而后将iap指针强制转换为Cat::*函数指针后,输出结果转变为Cat类中的eat函数,其原因是虚函数运行函数在运行阶段决定,与上述的动态函数的行为有决定性的差异。

​ 在撰写该文章时,借鉴了:如何使用指向类的成员函数的指针(详解!)一文,深刻的体会到了C++的博大精深,以及自己在输出这些概念时的不顺畅。希望自己日后多多精进。该文章中同事推荐了,深入探索C++对象模型,日后有机会必定拜读。