zoukankan      html  css  js  c++  java
  • Inside The C++ Object Model 读书笔记(三)——继承、数据和函数的存储

      也是好久不写了,忙着写报告、忙着这这那那的破事,其实很多时候觉得作为一个程序猿甚至只是一个IT行业相关的人,能够安心地、思路不被打乱地吃一本书

    或是写一段令自己满意的代码是一件多么幸福的事情。直奔主题吧,C++的设计可谓是博大精深,有许多人因为这样那样的原因吐槽C++,然而在Inside The C++

    Object Model一书中,作者Stanley B.Lippman展示了C++的一些看似令人费解的设计的原因,相信在了解这些原因以后,某些对C++存有偏见的人能够理性地看

    待C++的设计思想。这篇就主要记下最近的几点收获吧。

     

     1.先来讨论第一个关于继承的小问题:

      给定两个类,class Point2d,class Point3d,Point3d由Point2d继承而来,那么Point3d中到底有多少个成员变量,5个还是3个?如果是5个,怎么区分到底是从父类继承的成员x、y还是自身的x、y?

     1 class Point2d{
     2 public:
     3    int x;
     4    int y;  
     5 };
     6 
     7 
     8 class Point3d:public Point2d{
     9 public:
    10    int x;
    11    int y;         
    12    int z;
    13 };

      很简单要确认这点,只用sizeof一下就行了,得到的答案是sizeof(Point3d)=20,这样就可以确认是5个成员变量了,进一步实例化一个Point3d对象:

      Point3d ptr;

      使用ptr.x的方式(利用对象直接访问),很明显是调用的Point3d类型的成员x、而非Point2d的x成员,这是符合设计思想的(既然是特定唯一类的成员,当然取出的应该是这

    个类特有的成员)。那要怎么访问从父类继承的成员呢?

      可以这样:Point2d *pt = &ptr;  //这也就是C++多态的定义、用一个基类的指针寻址出一个子类对象

           pt->x;

      此时访问的就是从父类继承的成员x。这样就可以得出一个结论从父类继承的成员变量的名称和子类自身的成员变量名称允许重复,并且这是两个不同的成员。

      

      2.虚拟继承

      由继承的设计思想,就又引出了另外一个问题。假设class A :public father;  class B : public father; class C : public A, public B; 那么C的大小是多少?

      

          按照上面的想法,C++的在设计上,对于一个不含虚函数的类来说,似乎是子类的大小就是父类的大小再加上子类自身非静态成员变量的大小。没错,但这只适用于单一继

    承,对于多重继承,情况就不一样了。试想如果C的大小等于A+B+C(特有menmbers),那么C中不就包含了两个father类的大小么?很显然这是一个不靠谱的设计方式,会引进额

    外的存储空间,这不是大多数情况下所希望的。

      于是C++在设计上提供了一种解决方案:虚拟继承。这使得派生类如果继承基类多次,也只有一份基类的拷贝在继承类对象中。即:

      class father{...};

      class A:public virtual father{...};

      class B:public virtual father{...};

      class C:public A, public B{...};

      则C对象中只保存一个father类对象。一个class如果内含virtual base class则将被分割为两部分:一个不变区域和一个共享区域。不变区的数据为非虚基类的数据,而共享区就是虚基类数据。类中的虚函数表指针指向的虚函数表,正向偏移(向下)是寻址虚函数,负向偏移(向上)寻址的就是虚基类。还是以Point2d、Point3d类为例,Point3d由Point2d继承而来,指针_vptr_Point3d指向虚函数表。  


      3.指向成员变量的指针

      对于class Point3d的一个实例origin,&Point3d::z 和 &origin.z的结果是不同的,&Point3d::z应当为class Point3d内 member z的偏移量,而&origin.z 则为这个实例的member z 的地址。

      然而在Microsoft Visual C++10.0下,下面几条语句执行结果却很奇特。

      printf("%p\n",&Point3d::x);

      printf("%p\n",&Point3d::y);

      printf("%p\n",&Point3d::z);

      cout<<&Point3d::x<<endl;

      cout<<&Point3d::y<<endl;

      cout<<&Point3d::z<<endl;

      前三条分别为:0, 4 ,8 (已验证,即为成员变量的偏移地址,在意料之中),书中写在BCB3下结果分别为5,9,D,我没有验证;后三条结果均为1很奇特。合理的解释只能是VC++有过特殊处理了。

       好了就写到这里,继续学习吧~

  • 相关阅读:
    在HTML中使用JavaScript
    小强的HTML5移动开发之路(49)——HTML5开发神器HBuilder
    小强的HTML5移动开发之路(48)——(小练习)新闻订阅系统【1】
    QT学习记录之控件布局
    QT学习记录之理解信号槽机制
    小强的HTML5移动开发之路(47)——jquery mobile基本的页面框架
    Linux下MySQL备份以及crontab定时备份
    Linux管理日记(三)
    小强的HTML5移动开发之路(46)——汇率计算器【2】
    kindeditor实现ctrl+v粘贴word图片并上传
  • 原文地址:https://www.cnblogs.com/XiaoHDeBlog/p/3131300.html
Copyright © 2011-2022 走看看