《深度探索C++对象模型》第一章关于对象ObjectLessons之读书笔记

来源:互联网 发布:mac pro 贴膜涂层脱落 编辑:程序博客网 时间:2024/05/01 05:12

  C语言不支持数据和函数之间的关联性,称为procedural方法,由一组“分布在各个以功能为导向的函数中的”算法所驱动,它们处理的是共同的外部函数。C++是面向对象程序设计语言,利用封装、抽象、继承等特性,可以通过class hierarchy或者ADT风格来实现(同时实现参数化),而在C中实现对数据的操作则可以通过定义函数、前置宏或者在程序中直接完成。而这两种迥然有异的程序风格又给程序编写者带来深深的程序思考。在软件工程的理论中,有很多理论阐述了C++这种风格相较于C风格的优良性。而不可否认的是,在“使一个程序快速应战方面效率方面,C显然更具威力。毕竟C的吸引力在于它的精瘦和简易(相对于C++而言)”。而C++加上封装后的layout costs for adding encapsulation 以及存取时间的额外负担主要是由virtual引起的——virtual function机制(支持runtime binding)和virtual base class(实现多重继承中的base class有一个单一被共享的实例)。此外还有一些多重继承下的额外负担,比如派生类和其第二或后续之base class的转换等。然而,在其他方面,并无多少理由说明,C++就是比C更加庞大和迟缓。

  C++属于面向对象程序设计语言,而在C++中是如何表现这种模型模型的呢。它的发展历程分别是简单对象模型->表格驱动对象模型->C++对象模型。simple object model是一系列slot,每一个指向一个members,按声明顺序指定,每一个slot是指向的member的指针,将降低C++ compiler设计复杂度赔上空间和执行期的效率(这个slot或者索引的概念)应用到了C++中pointer-to-member。table-driven object model 则包含两个slot,一个指向存储data member的表格,一个指向存储 member function的表格(应用到了C++中virtual function中)。而比较成熟的却是将每一个nonstatic data members配置于每个object之中,静态数据成员存放在个别类对象之外。虚函数通过1.每一个class产生一堆指向virtual functions的指针,放在名为virtual table(vtbl)表格之中。2.每一个类对象放置一个指向相关virtual table的指针,名为vptr。而每一个类所关联的type_info_object(支持runtime type identification),放置于每一个类的vtbl第一个slot之中。在最初的模型中加上继承之后,无非是因为“间接次”的级数因为继承的深度而增加。在以后的笔记中将会继续讨论。而对象模型将会对于程序带来不同的结果。

  众所周知,C++有时会被人们称为"带类的C",在C++中引入了class这一“与众不同”的关键字。为了兼容C,C++中也存在struct这一关键字,而很多人都想过struct和class的区别以及什么时候该用struct代替class等问题。书中是这么描述“C程序员的巧计有时候会成为C++程序员的陷阱。例如把单一元素的数组放在一个struct的尾端,于是每个struct objects可以拥有可变大小的数组。而一个程序员迫切需要一个相当复杂的C+=某部分数据,可以最好抽取出来的一个独立的struct的声明”,可以用composition。struct可以将数据封装起来,并保证拥有与C兼容的空间布局,但是只在composition下才存在。而继承下,compiler将决定是否会有额外的data member被安插到base struct subobject中。

  C++程序设计模型支持三种programming paradigms。1.procedural model(程序模型)2.ADT3.object-oriented model    在写程序过程中,采用一种paradigm有助于整体行为的良好稳固。.

  C++支持以下三种多态:1.经由一组隐式的转换操作(如将一个派生类指针指向一个基类型指针)2.经由virtual机制3.经由dynamic_cast和typeid运算符。多态的主要用途是经由一个共同的接口来影响类型的封装。一个类对象的内存表现需要1.非静态数据成员的总和2.将数值调整到某数的倍数的空间3.支持virtual机制内部产生的overhead。

  一个指针,如论指向何种数据类型,指针本身所需的大小固定,在于所寻址处的object类型不同。这就可以解释void*指针不可通过它指向一个对象,因为它是未知涵盖地址空间,并不知其大小。cast实质上是编译器指令,并不改变地址,会改变“被指出内存大小和内容”的解释方式。

  一个基类对象类型,在编译时影响1.固定可用接口(只可以调用自身类的公有接口)2.该接口的access level。类型信息的封装并不是维护于对象之中,而是在link之中,link存在于“object的vptr”和“vptr”所指的“virtual table”。

  在如下的代码中(Bear是ZooAnimal的派生类)

Bear b;

ZooAnimal   za=b;、

   za并不是Bear且只能是ZooAnimal,多态所造成的“一个以上的类型”的潜在力量,并不能实际发挥在“直接存取objects”这件事情上.OO程序设计并不支持对object的直接处理。初始化函数将一个object的内容完整拷贝到另一个object之中。za的vptr不指向bear的virtual table,是因为编译器在(1)初始化(2)指定操作之间做出仲裁。编译器必须确保某个object含有一个或者一个以上的vptr,那些内容不会被base class object初始化或者改变。

  一个指针或引用之所以支持多态,是因为它们并不引发内存中“与类型有关的内存委托操作(type-dependent commitment)”。用派生类对象初始化基类对象,在编译时期解析一个“通过此object而触发的virtual function调用操作”,因而回避virtual机制,若将虚函数定义为inline,会获得更大效率的收获。

  多态需付出额外间接性,不论是“内存的获得”或“在类型的决断上”,通过指针或引用,这种风格称为“面向对象”。

  C++也支持ADT,非多态,速度快,空间紧凑,但是设计上无弹性。在C++中是ADT还是OO风格的选择上,应根据实际程序而决定。

  另外,global objects内存保证会在程序启动的时候被清为0.Local objects配置于程序的堆栈中,heap objects配置于自由空间中,却不一定被清为0,它们的内容将是内存上次被使用后的痕迹。。

  简单笔记,加理解。尚在努力学习中

0 0
原创粉丝点击