1. 首页 > 电脑教程 > 关于OO中继承的思考-深度认识类,对象,内存分配

关于OO中继承的思考-深度认识类,对象,内存分配

曾经在一本书中看到过这样的一个问题:(语言:C++)如果两个类,Parent和Child,Child继承Parent,Parent有一个函数print(),有成员变量pVar,Child中有两个函数print(),privatePrint(),有成员变量cVar,现在 ,申明一个Parent类型的指针来指向一个Child的对象。那么用这个指针可以调用privatePrint()函数吗?可以用这个指针可以调用成员cVar吗?答案是:不能。 那如果Parent中有一个虚函数privatePrint(),那么可以调用到privatePrint()吗?答案是:可以。首先我用C++试了一遍。C++的代码如下代码 1 #include 2 3 using namespace std; 4 5 class Prarent 6 { 7 public: 8 Prarent(){}; 9 int pVar;10 void print()11 {12 cout<<"I am parent"<< endl;13 }14 //virtual void privatePrint(){};15 };16 17 class Child : public Prarent18 {19 public:20 Child(){};21 int cVar;22 void print()23 {24 cout<<"I am child"<< endl;25 }26 void privatePrint()27 {28 cout<<"I am child' function privatePrint()"<< endl;29 }30 };31 32 void main()33 {34 Prarent *parent;35 Child child; // 原先我竟然写成 Child child = new Child(); 用久了C#, 都是C#惹的祸.36 parent = &child;37 parent->print();38 // parent->privatePrint(); 编译通不过. 39 // 1: 说明父类的指针虽然指向了子类的对象, 但是却不能引用父类没有申明的函数.40 // 2: 但是你将privatePrint申明为父类的虚函数, 则上面的可以运行成功.41 }42 43 其实之前我没有怎么在意这个问题。知道My sen Brother问了我。我才发现问题并非简单。经过了我们相互的讨论之后,我们得到了一种解释。我不敢保证一定完全是对的。但是却是我们自己的心得和体会。理解这个问题。首先要明白两个问题:1:类在内存是怎么存放的?(编译阶段实现)2:对象实例在内存中是怎么存放的?1:类在内存是怎么存放的?我根据以前Teacher Lei说过的一些内容,计算机组成,汇编语言,自己看过一些书,得到自己的一种思考。其中一个类,通过编译分别存在内存的两块地方,一个是代码段,一个是数据段;代码段存放一个类的成员变量;(ie. 上面的pVar和cVar都是存放在代码段中)数据段存放一个类的成员函数表;(ie. 上面的print()和privatePrint()都存放于这里);数据段中的每一个类的内存块应该由两个表填充,一个为虚函数表,一个为非虚函数表;数据段中类的成员变量存放,如下:int pVarint cVar代码段中类的成员函数表存放,如下: Parent()Print()Child()Print()pravtePrint()下面来解释 2:对象实例在内存中是怎么存放的?我们拿上面的例子来说明:当初始化一个Parent *parent 一个指针对象时,这时候parent所指向的地址就是100;vPtr(虚函数指针) 地址:100int pVar当初始化一个Child类型 child 对象时,这时候child的地址就是200;(注意和指针不一样)vPtr(虚函数指针) 地址:200int pVarint cVar首先:为什么这边一定是这样排列的还记得Teacher Lei说过吗?原因:子类在初始化的时候是先调用父类的构造函数!!说明父类的成员一定是先被初始化的。所以这边的结构必然是这样的。(这里很重要)好了。到现在基本就把要知道的基础知识解决了。不知道读着博客的人累了没有。。呵呵。后面的更精彩。现在 把child对象的地址赋值给指针parent(即 parent = &child)先来看看 parent->cVar 为什么不行?! 第一步:parent->cVar 其实是一个地址的偏移过程,现在parent的地址是200;那么cVar就代表2个偏移量;按说电脑是可以找到202的这个地址的值。可是为什么不行呢?第二步:在程序的编译过程中,每一个的成员函数名都被当做一个偏移量。就像这里,pVar代表量1个偏移。cVar代表2个偏移量。第三步:parent是Parent类型的。这个很关键。因为在编译的以后,parent已经初始化了一个最大偏移量max (这里的max为1)第四步:结果已经很明显了。因为parent的最大偏移量max 为1。程序根本找不着偏移量为2的变量地址。然后看看 parent->privatePrint() 为什么不行?还是那个关键点。parent是Parent类型的,还记得上面说类在内存中的加载方式吗?parent指向的是Parent的内存块。所以在那个内存块中,根本找不着privatePrint()这个函数。可是?有人因为会问了?为什么如果在Parent中申明了一个虚函数类型的privatePrint()就可以了呢?还记得类的实例在内存中加载的那个图吗?每个类的前面都有一个的vPtr虚函数指针,他指向的是当前类的虚函数表。通过虚函数表中的privatePrint()也相当一个指针,指向了子类中实现父类虚函数的privatePrint(),自然就能找到了。此时一切真相大白!!

声明:希维路由器教程网提供的内容,仅供网友学习交流,如有侵权请与我们联系删除,谢谢。ihuangque@qq.com
本文地址:https://www.ctrlcv.com.cn/diannao/169323484210707.html