读书笔记《深度探索c++对象模型》(1) - 对象模型基础

C++ 对象模型

  相对C语言的结构体和函数分离实现的不同,C++的对象模型基于类或继承,实现针对自身的从内存空间和存取时间做了优化或称为折中。
  (
1). C++对象模型的基本布局,如下图:   1.非静态数据成员在每个类对象中均有一份独立的实体,如:_x;   2.静态数据成员放置在类外,仅有一份共享实体,如:_point_count;   3.静态或非静态的成员函数也被放置在类外,仅有一份共享的函数实现体,如:PointCount()、Point(float)、x();   4.由一个称为虚函数表(Virtual table)的指向各个虚函数实现体(~Point()、print()),而该虚函数表又由每个类对象中插入的一个称为vptr的虚表指针对象指向该虚表(该指针由类的构造函数、拷贝赋值等初始化);另外虚表的第一个slot,其一般为一个指向type_info 对象(该对象以支持RTTI)。   优点:空间和存取时间的效率比较好的折中   缺点:类对象中的成员变量增、改、删都会导致必须重新编译程序 (2). 加上继承、虚拟继承后的C++对象模型   1.一般继承(非虚拟继承)时,父类的非静态数据成员均会叠加到子类的对象中;   2.虚拟继承(菱形继承)时,子类对象中的基类部分始终只会有一个;   3.子类如何与父类产生关系(注:还需要看不同编译器的具体实现方式):     非虚拟单继承时,父类的虚函数表被子类继承(或称为拷贝一份本类的副本虚表)(此时父类和子类各自有各自的虚函数表),若子类覆写了对应继承父类的虚函数,则在修改该继承来的虚函数对应slot指向一个新的函数体实现;
    又若子类新增加了虚函数,则新虚函数被增加到继承的虚表上的一个slot上。     非虚拟多重继承时,其子类将按照继承顺序的排列成员变量和指向继承的各父类虚表的vptr以及各类的虚表。     虚拟继承(菱形继承)时,除了会继承父类虚表外,若增加了新的虚函数,则会在子类中增加一个新的虚函数表指针vptr,此时可能出现两个vptr分别指向不同的虚表或者内含一个指针父类表,由该表分别指向各个基类的对象地址。     另外还有单一继承的虚继承(非菱形继承),可能各父类成员变量和指向父类的虚表vptr排列顺序不同,即子类对象的内存布局可能以vptr开始的而不是在最后,另外也继承各父类的虚表。 (
3)C++中class与struct的区别
  一般情况下,C
++中的struct主要为了兼容C的,但是在C++中,却还有另外的表现。   1.C++的类使用struct和class时的默认访问控制属性不同,前者默认为public,后者为private;   2.C++的类在默认继承关系中两者不同,struct默认为public,而class为private;   3.Class在模板关键字参数中可用,但struct不可替换它;   4.Class对象用{}初始化时(无构造函数)会失败,struct对象(仅是数据的结构体)时可以用{}来初始化,但若此时对该struct加入构造函数等也会导致失败;因两者的使用语义不同了,不再是兼容的C形式;   总结:一般情况下用class即可,若是仅仅包含数据或是必须用到兼容C数据结构时(用组合struct方式而非继承struct)可用struct。
(
4)一个类对象占用多少内存
  一般情况下:   
1.非静态的数据成员的总和大小;   2.加上任何由于对齐需要而填补的空间;   3.加上若有虚函数时的支持virtual而内部产生的任何额外的占用空间。
(
5)指针类型和void*指针类型
  一般情况下,指针类型或void
*类型占用空间一样,其指向的某个对象均被视作一个地址,而具体的某类型的指针可以教导编译器如何解释某个特定地址中的内存空间及其大小,而void*则无法识别,
  故而针对cast转型操作本质上是编译器指令,基本不会影响其所指向的地址,而仅仅是影响其解释方式而已。
(
6)基于(5)中描述的解释地址的方式,继承体系中,由指向子类对象的子类类型的指针和指向子类对象的父类类型的指针的区别
  
1.此两类指针均指向同一个对象,则指向的均是同一个地址;   2.此时编译器解释和能识别的分别为整个子类、仅父类部分(此时该指针无法直接操作子类的自身的成员,但可通过virtual机制);   3.另外就是子类对象赋值给基类对象可能会导致切割发生;   4.多重继承时,指向同一个子类对象的不同父类指针在编译阶段会重写调整指针指向,以支持指向正确的父类那块公共部分,同样的父类cast转型为子类时,编译器也会正确的调整指针指向实现指向正确的起始位置。
(
7)面向对象编程和基于对象编程、面向过程编程区别
  
1.面向过程比较简单,数据和函数处理逻辑分离,按照常规的逻辑顺序调用处理;   2.基于对象编程,则仅仅是对象的封装,增加一些操作行为的函数和属性保护等机制实现,如:string的实现,不支持扩展;   3.面向对象编程,则在基于对象的基础上,增加虚函数实现以及指向子类对象的父类类型的指针或引用来调用虚函数实现多态;   4.面向对象和基于对象除了以上的差别外,基于对象因不需要虚函数的支持,则数据在内存布局中更为紧凑,且更可能与C兼容,此外没有虚函数的支持,则在对象构建时没有额外的空间占用、分配和初始化虚表等负担;
   面向对象基于虚函数机制,更表现更为强大而灵活,更可扩展。

 

posted @ 2019-10-11 00:02  浩月星空  阅读(248)  评论(0编辑  收藏  举报