《深度探索C++对象模型》笔记(1)

本文深入探讨C++对象模型,解析面向对象编程的核心概念,包括封装、继承、多态的内存布局与实现机制,以及虚函数表和虚继承的作用。通过对比不同对象模型,阐述C++对象模型在空间和时间效率上的优势。
 

介绍

正如前几篇面试记录中各位面试官说的,了解如何使用还要明白一下其背后的原理,确实自己的知识欠缺太多,只是浮于表面,阅读本书来一次深度探索,将以往不知道的、模棱两可的尽可能的清晰化。话说科班的人好多都是找工作之前就看,而我是之后才开始,虽然晚了,但庆幸已经觉悟了。

马上2019,准备春招,加油

第一章 关于对象

读后感放前面

经过阅读以后,感觉本质面向对象语言就是人机交流的一种语言,而C++主要做的是将通过一系列关键词、自定义名称、符号构成的代码,转换成内存中的存储方式,实质就是面向对象这个哲学概念如何用内存中的布局、存储方式表示以及在代码中使用继承、多态等概念时是如何通过内存中的数据找到实际要运行的代码。

而这一章重点在说面向对象这个概念在C++这个语言中是如何实现的内存布局、存储,(当然还有不同编译器相应实现的差异)

OO,OB,virtual function,virtual base class

Object-Oriented OO面向对象–我理解的:封装、继承、多态
Object-Based OB基于对象–我理解的:只有封装

在传统的C程序中,采用的是过程式的思维:“数据”和“处理数据的操作(函数)”是分开来声明的,它们二者之间并没有关联性。但到了C++里,倾向于采用独立的“抽象数据类型”(ADT)来实现数据的封装。从代码结构上来看,C++似乎比C要消耗更高的时间和空间成本,但事实未必如此。相比于C,C++在布局和时间上主要的额外负担是由virtual引起的,这分为两部分:(非virtual,在布局和时间C++相比于C并没有太大的负担)
1.virtual function机制,用以支持一个有效率的“执行期绑定”,这里虚函数就是类声明中用的virtual void f(int a);这一类的声明
2.virtual base class,用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实例”。这个是虚继承class a:public b{},class c:virtual b{}

“抽象数据类型”(ADT)等程序设计范式(programming paradigms)

函数化程序设计、逻辑程序设计、语义数据模型、几何计算、数值计算、面向对象设计、原型设计、自然语言……

C++支持三种:程序模型(procedural model)C语言支持的、抽象数据类型模型(abstract data type model,ADT)、面向对象模型(Object-Oriented model)

对于抽象数据类型:

抽象数据类型(ADT)是一个实现包括储存数据元素的存储结构以及实现基本操作的算法。在这个数据抽象思想中,数据类型的定义和它的实现是分开的,这在软件设计中是一个重要的概念。这使得只研究和使用它的结构而不用考虑它的实现细节成为可能。ADT包括数据数据元素,数据关系以及相关的操作。

抽象数据类型需要通过固有数据类型(高级编程语言中已实现的数据类型)来实现。抽象数据类型是与表示无关的数据类型,是一个数据模型及定义在该模型上的一组运算。对一个抽象数据类型进行定义时,必须给出它的名字及各运算的运算符名,即函数名,并且规定这些函数的参数性质。一旦定义了一个抽象数据类型及具体实现,程序设计中就可以像使用基本数据类型那样,十分方便地使用抽象数据类型。

比如C++中,string类通过对+、等号操作符重载,实现对字符串的拼接、相等性判断。而C中需要strXXX(a,b)

C++对象模型

书里介绍了三种的C++对象模式:简单对象模型、表格驱动对象模型、C++对象模型。

简单对象模型:一个object(对象)表是一系列的slots(表格中的一格,一个条目,内存中一块地方),每个slot指向一个成员,成员按照声明顺序排列,成员包括数据成员和函数成员。这种方式object的存储区域只存放了一系列的“指针”分别指向每个member的位置。不把members直接存到object存储区域是为了避免不同member有不同的内存占用,而用“指针”的形势可以保证object的slots大小确定,并可通过索引确定每个member。这个模型并未使用,但保留了“指向成员的指针”观念。

这样的设计很简单,每个对象建立(实例化)的时候,根据class的声明可以快速建立出一个表,表内根据声明顺序存所有的函数、数据成员。但是对于一个class实例化两次应该就要建立两份,对于继承关系来说,子类的实例化后的slots中应该也重复包含了父类的slots。保证来设计简单,但在空间上、执行期效率上低。

执行期效率低:我想的是,多态的时候,子类用父类表示,那么父类没有子类那么多的member,需要重新建立一个映射索引?否则父类的pt[5]是第五个成员函数,但在子类的表中可能父类第五个成员函数已经成为的第8个,中间加了3个新的成员。而多重继承、虚函数的重写、出现继承串链将需要很多复杂操作。

表格驱动对象模型:一个object表只有两项分别指向两个表,一个指向Function Member Table存放所有成员函数的地址(只存地址),一个指向Member Data Table存放所有数据成员的实际值(存值)。这个模型未被使用,但将数据与函数分表存储的观念被保留。

应该也没解决有关多态的方式,对于成员函数表和简单对象模型是一样的,所以没有使用。

C++对象模型中:一个object表中有两类内容,第一类是所有的nonstatic data members非静态数据成员的值(注意是值)直接存在表中,非静态的所有每个实例之间不共用(如果有int* a;这样的成员,那么存储的也是值,因为指针也是个值,值的类型就是(int*)),第二类是指针,一个指针指向一个虚函数表(virtual table,vtbl),这个指针叫(vptr),vptr的设定和重置由constructor、destructor以及copy assignment运算符自动完成。
对于静态数据成员、非静态/静态成员函数放在object表外侧。
虚函数表中存储所有虚函数的指针,每一个class所关联的type_info object(用以支持runtime type identification,RTTI)也存放在virtual table之中一般放在vtbl的第一个slot。

静态数据成员、非静态/静态成员函数:这几个都是同一个class中不同实例共用的,且不会在运行时改变(指向这些的指针不变),所以把他们放在了object外吧。对于静态数据成员,实际上是存放在全局区的(未初始化前在全局区临近区域)。这样object table中就不用建立指向他们的指针了,因为在class的内存区域(class声明)中已经有了这些成员的具体位置,但是这应该也会导致一次跳转,class声明的内存中找对应的指针。

type_info object,运行时特征,应该是永久记录着这个对象在构建时是什么类型吧,这样在多态变化过程中也可以做到“不忘初心”,猜想:在通过dynamic_cast进行向下转型时的安全检查可能就是查看这个吧。

该模型的优点是,它的空间和存取时间的效率较高;缺点:如果应用程序本身的代码未改变,而所用到的class object的nonstatic data members有所修改,那么应用程序的代码同样需要重新编译(因为内存分布发生了改变)。同时也付出了空间和执行效率的代价(通过vptr先跳转到vtbl然后才能找到virtual,对于静态数据成员、非静态/静态成员函数我认为也是通过指针找到的,因为没有直接存在object table中,也就是只有nonstatic data members是直接访问,其他都要跳一次)

继承

public继承,对于出现多态(对virtual function重写),修改vtbl中的指针即可。

虚拟继承:不管base class在继承串链中被派生多少次,永远只会存在一个实例。如iostream中,iostream多重继承了ostream/istream,ostream/istream虚拟继承了ios,所以实例化iostream时只有一个ios实例。

也就是说每个object中还存了一个virtual base class table的表?或者或有一个表存了所有父类object的指针,这样才能避免重复的占用空间,也就导致了(“间接性”的级数将因为继承的深度而增加)

只有通过pointer、reference方式才可支持多态:
class B:classA {}
A a;//a是A
B b;//b是B
A c=b;//b被改成A类型,内容也是只有A了,不会在有B的内容。B重写A的方法,用c是调A的方法,因为不是引用或指针。译注:这会产生sliced切割。b原始是B类型占用空间大于c的A类型空间,多出的内容会被舍弃,但一个object的存储是先存储base object,逐级存储后续object,因此并不影响c调用A的方法。
A &d=b;//d还是B类型,调用还是B类型的方法内容,但无法调用B特有的方法,因为当做A类型来用了
A *e=b;//同上

class object有多大

注意,这是说一个对象的大小,不是class声明的大小,对于静态数据成员、非静态/静态成员函数是存在object外的,指向他们的指针也不需要存在object中,可以在class声明区域填写指针,因为这些指针的指向不会变。

大小包括:非静态数据成员的总和(对于指针类型数据成员,只是指针的大小)+对齐补位的空间+为支持virtual的空间

 

 

 

Techie亮博客,转载请注明:Coologic » 《深度探索C++对象模型》笔记(1)

Coologic 博客域名已更改,已从 www.techieliang.com 改为 www.coologic.cn,上述链接地址受到影响,若需查看源文请手动修改,多有不便,敬请谅解。
【最优潮流】直流最优潮流(OPF)课设(Matlab代码实现)内容概要:本文档主要围绕“直流最优潮流(OPF)课设”的Matlab代码实现展开,属于电力系统优化领域的教学与科研实践内容。文档介绍了通过Matlab进行电力系统最优潮流计算的基本原理与编程实现方法,重点聚焦于直流最优潮流模型的构建与求解过程,适用于课程设计或科研入门实践。文中提及使用YALMIP等优化工具包进行建模,并提供了相关资源下载链接,便于读者复现与学习。此外,文档还列举了大量与电力系统、智能优化算法、机器学习、路径规划等相关的Matlab仿真案例,体现出其服务于科研仿真辅导的综合性平台性质。; 适合人群:电气工程、自动化、电力系统及相关专业的本科生、研究生,以及从事电力系统优化、智能算法应用研究的科研人员。; 使用场景及目标:①掌握直流最优潮流的基本原理与Matlab实现方法;②完成课程设计或科研项目中的电力系统优化任务;③借助提供的丰富案例资源,拓展在智能优化、状态估计、微电网调度等方向的研究思路与技术手段。; 阅读建议:建议读者结合文档中提供的网盘资源,下载完整代码与工具包,边学习理论边动手实践。重点关注YALMIP工具的使用方法,并通过复现文中提到的多个案例,加深对电力系统优化问题建模与求解的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值