前言

借着这次学校的生产实习来回顾下C++的多态,这里讨论下C++的多态以及实现原理。我都是在QT下使用C++的,就直接在QT下进行演示了

多态介绍

面向对象语言中最核心的几个理念就是:封装、继承、多态,其中我感觉多态是真正的核心,第一第二个只是它的辅助。同时多态又是不容易懂的,所以在这就简单的介绍下啦(虽然我也懂得不多,呵呵)

静态联编

第一个简单的小程序(重载的多态)

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
#include <QCoreApplication>
#include <QDebug>
class Person {
public :
int age;
Person() : age(10){}
void getAge() {
qDebug() << "Person: " << age;
}
}; class VellBibi : public Person {
public :
int age;
VellBibi() : age(21){}
void getAge() {
qDebug() << "VellBibi: " << age;
}
}; int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); Person p;
p.getAge(); VellBibi v;
v.getAge(); Person *pPtr = &v;
pPtr->getAge(); return a.exec();
}

运行结果

说明

VellBibi类继承了Person类,VellBibi里重载了PersongetAge()方法,这样就实现了静态的多态,它的实现过程是在编译期间的。这种多态存在硬伤,就是当使用父类指针指向子类对象时,访问的是父类的东西,而不是子类的。这个算是C++的一个特性,在java里面就没有这个情况,因为java直接就是是动态联编,java的多态里面就不存在静态联编

动态联编

第二个简单的小程序(虚函数的多态)

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
#include <QCoreApplication>
#include <QDebug>
class Person {
public :
int age;
Person() : age(10){}
virtual void getAge() {
qDebug() << "Person: " << age;
}
}; class VellBibi : public Person {
public :
int age;
VellBibi() : age(21){}
virtual void getAge() {
qDebug() << "VellBibi: " << age;
}
}; int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); Person p;
p.getAge(); VellBibi v;
v.getAge(); Person *pPtr = &v;
pPtr->getAge(); return a.exec();
}

运行结果

说明

这个小程序和上一个一样,唯一变得就是在PersongetAge()方法前加了一个virtual关键字,VellBibi可加可不加,但最好加上。在C++中virtual关键字就是用来声明虚函数的,所谓虚函数就是虚的函数嘛,呵呵,就是这个函数不是在编译期间就确定下来了,而是在运行期间动态指定的。这就是导致这个小程序和上个小程序的运行结果不同的原因,当使用父类指针指向子类对象时,调用父类虚函数时系统会自动寻找到子类对象的函数。

接下来介绍下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
#include <QCoreApplication>
#include <QDebug>
class Person {
public :
int age;
Person() : age(10){}
void getAge() {
qDebug() << "Person: " << age;
}
}; class VellBibi : public Person {
public :
int age;
VellBibi() : age(21){}
void getAge() {
qDebug() << "VellBibi: " << age;
}
}; int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); Person p;
p.getAge(); VellBibi v;
v.getAge(); Person *pPtr = &v;
pPtr->getAge(); qDebug() << "PersonSize: " << sizeof(p);
qDebug() << "VellBibiSize: " << sizeof(v); return a.exec();
}

运行结果

说明

这个程序只是在第一个程序上加了sizeof(),看出来了神马?Person是4个字节,也就是int age;的字节数;VellBibi是8个字节,其实就是Person的字节数加上VellBibiint age;的字节数。

第四个小程序(动态联编时对象的大小)

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
#include <QCoreApplication>
#include <QDebug>
class Person {
public :
int age;
Person() : age(10){}
virtual void getAge() {
qDebug() << "Person: " << age;
}
}; class VellBibi : public Person {
public :
int age;
VellBibi() : age(21){}
virtual void getAge() {
qDebug() << "VellBibi: " << age;
}
}; int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); Person p;
p.getAge(); VellBibi v;
v.getAge(); Person *pPtr = &v;
pPtr->getAge(); qDebug() << "PersonSize: " << sizeof(p);
qDebug() << "VellBibiSize: " << sizeof(v); return a.exec();
}

运行结果

说明

这个程序只是在第二个程序上加了sizeof(),这时会发现Person变8个字节了,为神马呢?这是两个int型的大小啊,why?好了不卖关子了,这是静态Person的大小加上一个指针的大小,那哪来的指针呢?在Person里面也没有定义啊!呵呵,这是C++编译器自动加上的,加上用来动态指定的,只要存在virtual关键字的类最上面都是有一个这样的指针,指向一个vtable虚拟表,里面记录着这个类所有包含的虚函数地址。

动态联编内存示例图:

第五个小程序(动态联编内存分析)

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
#include <QCoreApplication>
#include <QDebug>
class Person {
public :
int age;
Person() : age(10){}
virtual void getAge() {
qDebug() << "Person: " << age;
}
}; class VellBibi : public Person {
public :
int age;
VellBibi() : age(21){}
virtual void getAge() {
qDebug() << "VellBibi: " << age;
}
}; int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); Person p;
VellBibi v; int *pfv = (int *)&p;
int *vfv = (int *)&v;
qDebug() << "PersonFirstValue: " << *pfv;
qDebug() << "VellBibiFirstValue: " << *vfv; qDebug() << "PersonSecondValue: " << *(pfv + 1);
qDebug() << "VellBibiSecondValue: " << *(vfv + 1); qDebug() << "PersonThirdValue: " << *(pfv + 2);
qDebug() << "VellBibiSThirdValue: " << *(vfv + 2); int *pt = (int *)*pfv;
int *vt = (int *)*vfv;
qDebug() << "PersonVTableFirstValue: " << *pt;
qDebug() << "VellBibiVTableFirstValue: " << *vt; qDebug() << "PersonVTableSecondValue: " << *(pt + 1);
qDebug() << "VellBibiVTableSecondValue: " << *(vt + 1);
return a.exec();
}

运行结果

说明

将对象第地址强制转换成int型指针来探寻对象内部数据的情况。前两行是对象的头地址的值,很明显这是一个指针;3、4行是第二个值,都是Personint age;变量; 5行是一个随机值,说明Person对象里面其实就只有一个int age;变量而已,其实在C++中类的实现也就是一个C语言的struct而已。再将头地址指向的vtable里的值取出来看看,7、8行就是各自vtable的第一个值,可以看出还是一个指针,指向的肯定是代码段相对应的函数啦~这两个指针指向了不同的函数,这也就是动态联编啦~当然还需要一个程序来说明,等会再说;再看看9、10行,都是0,也就是NULL很好理解,是不是似曾相识啊,没错就是字符串里的结束符啦!

第六个小程序(动态联编的美妙)

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
#include <QCoreApplication>
#include <QDebug>
class Person {
public :
int age;
Person() : age(10){}
virtual void getAge() {
qDebug() << "Person: " << age;
}
}; class VellBibi : public Person {
public :
int age;
VellBibi() : age(21){}
virtual void getAge() {
qDebug() << "VellBibi: " << age;
}
}; int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); Person p;
VellBibi v;
Person *vp = &v; int *pfv = (int *)&p;
int *vfv = (int *)vp;
qDebug() << "PersonFirstValue: " << *pfv;
qDebug() << "VellBibiFirstValue: " << *vfv;
qDebug(); qDebug() << "PersonSecondValue: " << *(pfv + 1);
qDebug() << "VellBibiSecondValue: " << *(vfv + 1);
qDebug(); qDebug() << "PersonThirdValue: " << *(pfv + 2);
qDebug() << "VellBibiSThirdValue: " << *(vfv + 2);
qDebug(); int *pt = (int *)*pfv;
int *vt = (int *)*vfv;
qDebug() << "PersonVTableFirstValue: " << *pt;
qDebug() << "VellBibiVTableFirstValue: " << *vt;
qDebug(); qDebug() << "PersonVTableSecondValue: " << *(pt + 1);
qDebug() << "VellBibiVTableSecondValue: " << *(vt + 1); return a.exec();
}

运行结果

说明

这个应该很好理解了,只是定义了一个Person的指针vp,指向了VellBibi的对象v,然后将&v换成了vp,其他的都没变;也就是使用父类指针指向子类对象,结果可以看出和第五个程序是一样的,说明了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
#include <QCoreApplication>
#include <QDebug>
class Person {
public :
int age;
Person() : age(10){}
virtual void getAge() {
qDebug() << "Person: " << age;
}
}; class VellBibi : public Person {
public :
int suiShu;
VellBibi() : suiShu(21){}
virtual void getAge() {
qDebug() << "VellBibi: " << suiShu;
}
}; int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); Person p;
VellBibi v; Person *pptr = &p; int *pp = (int *)&p;
int *vp = (int *)&v; *pp = *vp; pptr->getAge(); return a.exec();
}

运行结果

问题分析

结果可以看出该打印的suiShu变随机数了;我这里使用了点小计俩,我先将对象的头地址当做int看待,然后将VellBibi对象的头地址赋值给了Person对象,最后使用Person对象的指针去调它自己的函数,会发现出错了。上内存分析图吧:

没有改变Person对象头指针

改变了Person对象头指针后

继续分析,Person对象的vtable指针指向了VellBibivtable,当使用指针去访问时虚函数时就会做动态联编,Person对象会找到VellBibivtable然后找到了VellBibigetAge(),而VellBibigetAge()回去调它自己的变量也就是suishu变量,这下就出问题了,Person对象哪来的suishu变量?所以就系统就在那个不是Person对象内容的地方取了个值,当然这是随机的啦


结束语

写这篇帖子是为了明天实习的每人一讲啦~不过更是一种分享,我是从别人那得到的这份知识,想更好的让更多的人去收获这份知识,这就是分享的意义;当得知我做的努力给别人带来了帮助,我会非常高兴,这就是分享的乐趣。一起分享一起学习,Come on~~~

原文地址: http://vview.ml/2014/07/20/polymorphisn.html
written by Vell Bibi posted at VBlog

C++多态分析(polymorphisn)的更多相关文章

  1. 深度分析:理解Java中的多态机制,一篇直接帮你掌握!

    Java中的多态 1 多态是什么 多态(Polymorphism)按字面的意思就是"多种状态".在面向对象语言中,接口的多种不同的实现方式即为多态.用白话来说,就是多个对象调用同一 ...

  2. 对Java中多态,封装,继承的认识(重要)

                                                            一.Java面向对象编程有三大特性:封装,继承,多态 在了解多态之前我觉得应该先了解一下 ...

  3. C++三大特性之多态

    原文地址:https://qunxinghu.github.io/2016/09/08/C++%20%E4%B8%89%E5%A4%A7%E7%89%B9%E6%80%A7%E4%B9%8B%E5%A ...

  4. C++多态有哪几种方式?

    C++多态方式: (1)静态多态(重载,模板) 是在编译的时候,就确定调用函数的类型. (2)动态多态(覆盖,虚函数实现) 在运行的时候,才确定调用的是哪个函数,动态绑定.运行基类指针指向派生类的对象 ...

  5. C++ 系列:C++ 对象模型

    1      何为C++对象模型 C++对象模型可以概括为以下2部分: 1.语言中直接支持面向对象程序设计的部分: 2.对于各种支持的底层实现机制 语言中直接支持面向对象程序设计的部分,如构造函数.析 ...

  6. C++对象模型详解

    原文链接:吴秦大神的C++对象模型. 何为C++对象模型? C++对象模型可以概括为以下2部分: 1.语言中直接支持面向对象程序设计的部分: 2.对于各种支持的底层实现机制. 语言中直接支持面向对象程 ...

  7. C++对象模型(虽然在GCC下很大的不同,但是先收藏)

    C++对象模型 何为C++对象模型? 部分: 1.        语言中直接支持面向对象程序设计的部分 2.        对于各种支持的底层实现机制 语言中直接支持面向对象程序设计的部分,如构造函数 ...

  8. C++对象模型6--对象模型对数据访问的影响

    如何访问成员? 前面介绍了C++对象模型,下面介绍C++对象模型的对访问成员的影响.其实清楚了C++对象模型,就清楚了成员访问机制.下面分别针对数据成员和函数成员是如何访问到的,给出一个大致介绍. 对 ...

  9. C++对象模型--C++对象模型

    何为C++对象模型? 部分: 1       语言中直接支持面向对象程序设计的部分 2       对于各种支持的底层实现机制 语言中直接支持面向对象程序设计的部分,如构造函数.析构函数.虚函数.继承 ...

随机推荐

  1. Tomcat集群配置学习篇-----分布式应用

    Tomcat集群配置学习篇-----分布式应用 现目前基于javaWeb开发的应用系统已经比比皆是,尤其是电子商务网站,要想网站发展壮大,那么必然就得能够承受住庞大的网站访问量:大家知道如果服务器访问 ...

  2. Buffer数据结构和new IO的Memory-mapped files

    一.Buffer类 java.nio.Buffer这个类是用来干什么的?有怎样的结构? "Core Java"中是这样定义的“A buffer is array of values ...

  3. Map.putAll方法——追加另一个Map对象到当前Map集合(转)

    该方法用来追加另一个Map对象到当前Map集合对象,它会把另一个Map集合对象中的所有内容添加到当前Map集合对象. 语法  putAll(Map<? extends K,? extends V ...

  4. C#Winfrom中,窗体加载时会自动执行一次控件的textchange事件,怎么让它不执行?

    http://zhidao.baidu.com/link?url=iTSyfa5_RJBSb37S8efdWoL5eDMrnxeAm-prhGSNBXqdP9r7PzNDQTc7gVzJgCNdzli ...

  5. PHP输出缓冲控制- Output Control 函数应用详解

    说到输出缓冲,首先要说的是一个叫做缓冲器(buffer)的东西.举个简单的例子说明他的作用:我们在编辑一篇文档时,在我们没有保存之前,系统是不会向磁盘写入的,而是写到buffer中,当buffer写满 ...

  6. BZOJ 1898 Swamp 沼泽鳄鱼(矩阵)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1898 题意:一个无向图.给出起点和终点,以及某些时刻某些点不能到达的信息.问从起点出发在 ...

  7. 你用哪种工具进行iOS app自动化功能测试?

    原文见http://www.cocoachina.com/applenews/devnews/2013/1111/7332.html

  8. Android布局详解之一:FrameLayout

      原创文章,如有转载,请注明出处:http://blog.csdn.net/yihui823/article/details/6702273 FrameLayout是最简单的布局了.所有放在布局里的 ...

  9. SPRING STS Virgo OSGI 开发一 : bundle 项目的创建

    1. Spring STS 下载地址  (spring 最近改了站点 暂时不是太熟悉)     http://spring.io/tools/sts/all 2. 下载 Virgo 插件    htt ...

  10. ASP.NET MVC 学习2、从Controller传递数据到View

      参考:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-view 一,Control ...