1、什么是多态
多态性可以简单概括为“一个接口,多种行为”。
也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。这是一种泛型技术,即用相同的代码实现不同的动作。这体现了面向对象编程的优越性。
多态分为两种:
(1)编译时多态:主要通过函数的重载和模板来实现。
(2)运行时多态:主要通过虚函数来实现。
2、几个相关概念
(1)覆盖、重写(override)
override指基类的某个成员函数为虚函数,派生类又定义一成员函数,除函数体的其余部分都与基类的成员函数相同。注意,如果只是函数名相同,形参或返回类型不同的话,就不能称为override,而是hide。
(2)重载(overload)
指同一个作用域出生多个函数名相同,但是形参不同的函数。编译器在编译的时候,通过实参的个数和类型,选择最终调用的函数。
(3)隐藏(hide)
分为两种:
1)局部变量或者函数隐藏了全局变量或者函数
2)派生类拥有和基类同名的成员函数或成员变量。
产生的结果:使全局或基类的变量、函数不可见。
3、几个简单的例子
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
|
/******************************************************************************************************
* File:PolymorphismTest
* Introduction:测试多态的一些特性。
* Author:CoderCong
* Date:20141114
* LastModifiedDate:20160113
*******************************************************************************************************/
#include "stdafx.h"
#include <iostream>
using namespace std;
class A
{
public :
void foo()
{
printf ( "1 " );
}
virtual void fun()
{
printf ( "2 " );
}
};
class B : public A
{
public :
void foo() //由于基类的foo函数并不是虚函数,所以是隐藏,而不是重写
{
printf ( "3 " );
}
void fun() //重写
{
printf ( "4 " );
}
};
int main( void )
{
A a;
B b;
A *p = &a;
p->foo(); //输出1。
p->fun(); //输出2。
p = &b;
p->foo(); //输出1。因为p是基类指针,p->foo指向一个具有固定偏移量的函数。也就是基类函数
p->fun(); //输出4。多态。虽然p是基类指针,但实际上指向的是一个子类对象。p->fun指向的是一个虚函数。按照动态类型,调用子类函数
return 0;
}
|
4、运行时多态以及虚函数的内部实现
看了上边几个简单的例子,我恍然大悟,原来这就是多态,这么简单,明白啦!
好,那我们再看一个例子:
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
|
class A
{
public :
virtual void FunA()
{
cout << "FunA1" << endl;
};
virtual void FunAA()
{
cout << "FunA2" << endl;
}
};
class B
{
public :
virtual void FunB()
{
cout << "FunB" << endl;
}
};
class C : public A, public B
{
public :
virtual void FunA()
{
cout << "FunA1C" << endl;
};
};
int _tmain( int argc, _TCHAR* argv[])
{
C objC;
A *pA = &objC;
B *pB = &objC;
C *pC = &objC;
printf ( "%d %d " , &objC, objC);
printf ( "%d %d " , pA, *pA);
printf ( "%d %d " , pB, *pB);
printf ( "%d %d " , pC, *pC);
return 0;
}
|
运行结果:
5241376 1563032
5241376 1563032
5241380 1563256
5241376 1563032
细心的同志一定发现了pB出了问题,为什么明明都是指向objC的指针,pB跟别人的值都不一样呢?
是不是编译器出了问题呢?
当然不是!我们先讲结论:
(1)每一个含有虚函数的类,都会生成虚表(virtual table)。这个表,记录了对象的动态类型,决定了执行此对象的虚成员函数的时候,真正执行的那一个成员函数。
(2)对于有多个基类的类对象,会有多个虚表,每一个基类对应一个虚表,同时,虚表的顺序和继承时的顺序相同。
(3)在每一个类对象所占用的内存中,虚指针位于最前边,每个虚指针指向对应的虚表。
先从简单的单个基类说起:
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
|
class A
{
public :
virtual void FunA()
{
cout << "FunA1" << endl;
}
virtual void FunA2()
{
cout << "FunA2" << endl;
}
};
class C : public A
{
virtual void FunA()
{
cout << "FunA1C" << endl;
}
};
int _tmain( int argc, _TCHAR* argv[])
{
A *pA = new A;
C *pC = new C;
typedef void (*Fun)( void );
Fun fun= (Fun)*(( int *)(*( int *)pA));
fun(); //pA指向的第一个函数
fun = (Fun)*(( int *)(*( int *)pA) +1);
fun(); //pA指向的第二个函数
fun = (Fun)*(( int *)(*( int *)pC));
fun(); //pC指向的第一个函数
fun = (Fun)*(( int *)(*( int *)pC) + 1);
fun(); //pC指向的第二个函数
return 0;
}
|
运行结果:
FunA2
FunA1C
FunA2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
int _tmain( int argc, _TCHAR* argv[])
{
C objC;
A *pA = &objA;
B *pB = &objC;
C *pC = &objC;
typedef void (*Fun)( void );
Fun fun = (Fun)*(( int *)(*( int *)pC));
fun(); //第一个表第一个函数
fun = (Fun)*(( int *)(*( int *)pC)+1);
fun(); //第一个表第二个函数
fun = (Fun)*(( int *)(*(( int *)pC+1)));
fun();<span style= "white-space:pre" > </span> //第二个表第一个函数
fun = (Fun)*(( int *)(*( int *)pB));
fun(); //pB指向的表的第一个函数
return 0;
}
|
哈哈,和我们的猜测完全一致:
FunA2
FunB
FunB
以上就是小编为大家带来的C++中的多态与虚函数的内部实现方法全部内容了,希望大家多多支持服务器之家~