《深度探索C对象模型》读书笔记 二.docx

上传人:b****8 文档编号:13040432 上传时间:2023-06-10 格式:DOCX 页数:11 大小:23.10KB
下载 相关 举报
《深度探索C对象模型》读书笔记 二.docx_第1页
第1页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第2页
第2页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第3页
第3页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第4页
第4页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第5页
第5页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第6页
第6页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第7页
第7页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第8页
第8页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第9页
第9页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第10页
第10页 / 共11页
《深度探索C对象模型》读书笔记 二.docx_第11页
第11页 / 共11页
亲,该文档总共11页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

《深度探索C对象模型》读书笔记 二.docx

《《深度探索C对象模型》读书笔记 二.docx》由会员分享,可在线阅读,更多相关《《深度探索C对象模型》读书笔记 二.docx(11页珍藏版)》请在冰点文库上搜索。

《深度探索C对象模型》读书笔记 二.docx

《深度探索C对象模型》读书笔记二

《深度探索C对象模型》读书笔记二

2002-7-63.3DataMember的存取

1.不管什么情况,每一个staticdatamember只有一个实体,放在程序的datasegment之中,每次程序取用staticmember,不管是通过operator:

还是memberselectionoperator,都会被内部转化为对该唯一extern实体的直接参考操作。

每一个staticmember的存取以及与class的关联不会导致任何执行时间或空间上的额外负担。

如果有两个classes,每一个都声明了一个staticmemberfreeList,那么当它们都放在程序的datasegment时,就会导致名称冲突,编译器的解决方法是使用name-mangling,暗中对每一个staticdatamember编码,以获得一个独一无二的程序识别代码。

2.有多少个编译器,就有多少种name-mangling做法,任何name-mangling做法都有两个要点:

ü一种算法,推导出独一无二的名称;

ü如果编译系统或者环境工具必须和使用者交谈,那些独一无二的名称可被轻易推导回原先的名称。

3.取一个staticdatamember的地址,会得到一个指向其数据类型的常量指针,而不是指向其classmember的指针。

4.nonstaticdatamembers直接放在每一个classobject之中,除非经过显示的explicit或隐含的implicitclassobject,没有办法直接存取它们。

只要程序员在一个memberfunction中直接处理一个nonstaticdatamember,所谓implicitclassobject就会发生,其实质是编译器会为这个memberfunction增添一个constthis指针,而在函数体内通过这个this指针来存取nontaticdatamember。

5.欲对一个nonstaticdatamember进行存取操作,编译器需要把classobject的起始地址加上datamember的编译量offset,如地址&someObject.someMember等于&someobject+(&theClass:

someMember–1);指向datamember的指针,其offset值总是会被加上1,这样可以使编译系统区分出一个指向class第一个datamember的指针和一个没有指向任何datamember的指针。

6.每一个nonstaticdatamember的偏移量在编译时期即可获知,甚至如果member属于一个单一或多重继承体系中baseclasssubobject也是一样,因此其存取效率和一个Cstructmember或一个nonderivedclass的member的存取效率是一样的。

但是在虚拟继承的情况下就另当别论了:

如果该nonstaticdatamember是一个virtualbaseclass的member,并且通过指针来存取的话,在编译时期就不会得知这个member真正的offset位置,所以这个存取操作必须延迟至执行期,经由一个额外的间接导引才能够解决。

2002-7-73.4"继承"与DataMember1.在C++继承模型中,一个derivedclassobject所表现出来的东西,是其自己的members加上其baseclassesmembers的总和。

C++并未规定derivedclassmembers和baseclassesmembers的排列次序。

不过,在大部分编译器上,除virtualbaseclass外,baseclassmembers总是先出现。

2.一般而言,具体继承concreteinheritance并不会增加空间或存取时间上的额外负担。

3.把两个原本独立不相干的classes凑成一对type/subtype,并带有继承关系容易犯两个错误。

一是可能会重复设计一些相同操作的函数,一般而言,选择某些函数做成inline函数,是设计class的一个重要课题;二是把一个class分解为多层,有可能会为了表现class体系之抽象化,因为编译器的边界调整而膨胀所需空间。

其根本原因是C++保证出现在derivedclass中的baseclasssubobject有其完整原样性。

4.C++最初问世时,许多编译器把vptr放在classobject的尾端,这样可以保留baseclassCstruct的对象布局。

此后,某些编译器开始把vptr放在classobject的开始处,这样会给多重继承下通过指向classmembers之指针调用virtualfunction带来一些帮助,否则,在执行期不仅必须备妥从classobject起点处开始量起的offset,而且必须备妥classvptr之间的offset。

5.单一继承提供了一种自然多态的形态,是关于class体系中basetype和derivedtype之间的转换。

一般来说,baseclass和derivedclassobjects都是从相同的地址开始。

但若将vptr放在classobject的起始处,如果baseclass没有virtualfunction而derivedclass有,那么单一继承的自然多态就会打破。

此时,把一个derivedobject转换为其base类型就需要编译器的介入,用以调整地址。

而在既是多重继承又是虚拟继承的情况下,编译器的介入则更有必要。

6.多重继承的复杂度在于derivedclass和其上一个baseclass乃至上上一个baseclass之间的非自然关系,其主要问题发生在derivedclassobjects和其第二或后继的baseclassobjects之间的转换。

对一个多重派生对象,将其地址指定给最左端baseclass的指针,情况将和单一继承相同,而第二个或后继的baseclass的地址指定操作则需要修改地址,加上或减去(若是downcast)介于中间的baseclasssubobjects的大小。

C++并未要求多重继承时derivedclassobject中各个baseclasssubjectes的排列次序,目前各个编译器都是根据声明次序来排列它们。

7.class内如果内含一个或多个virtualbassclasssubobjects,将被分割为两部分:

一个不变局部和一个共享局部。

不变局部总是拥有固定的offset,其数据用以指定共享局部的位置,可以直接存取;而共享局部表现的就是virtualbaseclasssubobject,其位置会因为每次的派生操作而变化,只可间接存取。

各家编译器实现技术之间的差异就在于间接存取的方法不同。

8.一般而言,virtualbaseclass最有效的一种运用方式是:

一个没有任何datamember的抽象class。

2002-7-143.5对象成员的效率

如果没有把优化开关打开,就很难猜测一个程序的效率表现,因为程序代码潜在性的受到某些与编译器有关的东西的影响。

程序员如果关心效率,应该实际测试,不要光凭推论或常识判断或假设。

优化操作并不一定总是能够有效运行。

2002-7-153.6指向DataMembers的指针

指向datamembers的指针可用来详细调查classmembers的底层布局,可用来决定vptr是放在class的起始处还是尾端,还可用来决定class中accesssections的次序。

取一个nonstaticdatamember的地址,将会得到它在class的offset;而取一个staticdatamember的地址或者取一个绑定于真正classobject身上的datamember的地址,将会得到该member在内存中的真正地址。

这也正是someTypesomeClass:

*和someTye*潜在的区别。

2002-7-16Function语意学TheSemanticsofFunctionC++支持三种类型的memberfunctions:

static、nonstatic和virtual,每一种类型的调用方式都不同。

4.1Members的各种调用方式

1.C++的设计准则之一便是nonstaticmemberfunction至少必须和一般的nonmemberfunction有着相同的效率。

编译器内部会将member函数实体转换为对等的nonmember函数实体,其步骤为:

ü改写函数原型signature以安插一个额外的参数this到memberfunction中,使得classobject可以调用该函数。

其中,this是const指针,若该函数为const,则反映在this上面的结果是this指向的data也为const;

ü将每一个对nonstaticdatamember的存取操作改为经由this指针来存取;

ü将memberfunction重新写成一个外部函数,对函数名称进行mangling处理;

此后,每一个函数调用操作也都必须转换,用以提供相应的实参。

2.关于虚拟函数的内部转换步骤:

若normalize是一个virtualmemberfunction,ptr-normalize();会被内部转化为(*ptr-vptr[t])(ptr);事实上,vptr名称也会被mangled,因为可能存在有多个vptrs;t是vitrualtableslot的索引值,关联到normalize函数;第二个ptr表示this指针。

3.使用classscopeoperator明确调用一个vitualfunction,或经由一个classobject调用一个vitualfunction其决议方式会和nontaticmemberfunction一样!

故virtualfunction的一个inline函数实体可被扩展开来,因而提供极大的效率利益。

4.staticmemberfunction的主要特征是没有this指针,这导致它不能直接存取其class中的nonstaticmembers,不能被声明为const、volatile或virtual,也不需要经由classobject才能调用。

staticmemberfunction会被提出于class声明之外,并给予一个经过mangled的适当名称。

如果取一个staticmemberfunction的地址,得到的将是其在内存中的地址,其地址类型并不是一个指向classmemberfunction的指针,而是一个nonmember函数指针。

staticmemberfunction的一个意想不到的好处是可以成为一个callback函数,也可以成功地应用在thread函数身上。

2002-07-174.2VirtualMemberFunctions虚拟成员函数

1.C++中,多态polymorphism表示以一个publicbaseclass指针或reference寻址出一个derivedclassobject。

识别一个class是否支持多态,唯一适当的方法试看它是否有任何virtualfunction。

只要class拥有一个virtualfunction,它就需要一份额外的执行期型别判断信息。

2.一个class只会有一个virtualtable,其中内含对应classobject中所有的activevirtualfunctions的函数实体的地址。

这些activevirtualfunctions包括:

ü一个class定义的函数实体。

它会改写overriding一个可能存在的baseclassvirtualfunction。

ü继承自baseclass的函数实体。

此时该class不改写baseclassvirtualfunction。

ü一个pure_virtual_called()函数实体,它既可以扮演purevirtualfunction的空间保卫者,也可以当作执行期异常处理函数。

如果该函数被调用,通常的操作是结束程序。

3.每一个virtualfunction都被指派一个固定不变的索引值,该值在整个继承体系中保持与特定virtualfunction的关联。

这样就可以在编译时期设定virtualfunction的调用。

2002-7-204.多重继承下,一个上层basseclasses数目为n的derivedclass,它将内含n-1个额外的virtualtables。

其主要实体与最左端的baseclass共享,其中包含所有virtualfunctios的地址;n-1个次要实体与其它baseclasses有关,其中只包含出现在对应baseclass中virtualfunctions的地址。

5.在多重继承中支持virtualfunction,其复杂度围绕在第二个及后继baseclass上,以及执行期this指针调整上。

第二(或后继)baseclass会影响对virtualfunction支持的3种情况:

ü通过指向第二个baseclass的指针,调用derivedclassvirtualfunction;

ü通过指向derivedclass的指针,调用第二个baseclass中一个继承而来的virtualfunction;

ü允许virtualfunction函数的返回值类型有所变化,可能是basetype,也可能是publiclyderivedtype。

6.关于执行期this指针调整比较有效率的解决方法是thunk。

所谓thunk是一小端assembly码,用来以适当的offset值来调整this指针并跳到相应的virtualfunction。

thunk技术允许virtualtableslot继续内含一个简单的指针,此时多重继承将不需要任何空间上的额外负担!

slots中的地址可以直接指向virtualfunction,也可以指向一个相关的thunk。

4.3函数的效能

nonmember、staticmember和nonstaticmemberfunction在内部都会转化为完全相同的形式,三者效率相同。

2002-08-084.4指向MemberFunction的指针

对一个nonstaticmemberfunction取址,得到的是该函数在内存中的地址;而面对一个virtualfunction,得到的将是一个索引值。

这个值是不完整的,必须被绑定于一个classobject上,才能够通过它调用函数。

指向memberfunction的指针的声明语法,以及指向memberselection运算符的指针,其作用是作为this指针的空间保留者。

因此,staticmemberfunction的类型是函数指针,而不是指向memberfunction的指针。

使用一个memberfunction指针,如果并不用于virtualfunction、多重继承、virtualbaseclass等情况的话,其成本并不比使用一个nonmemberfunction指针要高。

4.5InlineFunctions

关键词inline只是一项请求。

如果在某个层次上,函数的执行成本比一般的函数调用及返回机制所带来的负荷低,那么该请求被接受,编译器就用一个表达式合理地将函数扩展开来。

真正的inline函数扩展操作是在函数调用的那一点上。

在inline扩展期间,每一个形式参数会被对应的实际参数所取代,inline函数中的每一个局部变量都必须被放在函数调用的一个封闭区段中,并拥有一个独一无二的名称。

这会带来参数的求值操作以及临时性对象的管理。

2002-08-11

构造、解构、拷贝语意学SemanticsofConstruction,Destruction,andCopy1.一般而言,class的datamember应该被初始化,而且只在constructor中或其它memberfunctions中初始化,其它任何操作都将破坏其封装性质,使其维护和修改更加困难。

2.可以定义并调用invoke一个purevirtualfunction,但它只能被静态调用,不能经由虚拟机制调用。

每一个derivedclassdestructor会被编译器加以扩展,静态调用每一个virtualbaseclass以及上一层baseclass的destructor。

因此,不管baseclass的virtualdestructor是否声明为pure,它必须被定义。

5.1无继承情况下的对象构造

C++Standard要求编译器尽量延迟nontrivialmembers的实际合成操作,直到真正遇到其使用场所为止。

5.2继承体系下的对象构造

一般而言,继承体系下编译器对constructor所作的扩充操作以及次序大约如下:

ü所有virtualbaseclassconstructors必须从左到右、从深到浅被调用:

如果class被列于memberinitializationlist中,那么任何明确指定的参数都必须传递过去,否则如果class有一个defaultconstructor,也应该调用它;class中的每一个virtualbaseclasssubobject的偏移量offset必须在执行期可被存取;如果classobject是最底层most-derived的class,其constructors可能被调用,某些用以支持这个行为的机制必须被方进来。

ü以baseclass的声明次序调用上一层baseclassconstructors:

如果baseclass被列于memberinitializationlist中,那么任何明确指定的参数都必须传递过去,否则若它有defaultconstructor或defaultmemberwisecopyconstructor,那么就调用它;如果baseclass是多重继承下的第二或后继的baseclass,那么this指针必须有所调整。

ü如果classobject有virtualtablepointer(s),它(们)必须被设定初值,指向适当的virtualtable(s)。

ü如果有一个member没有出现在memberinitializationlist中,但它有defaultconstructor,调用之。

ü将memberinitializationlist中的datamembers的初始化操作以members的声明次序放进constructor的函数本身。

2002-8-185.3对象复制语意学ObjectCopySemantics1.只有在默认行为所导致的语意不安全或者不正确以致发生别名化aliasing或者内存泄漏memoryleak时,才需要设计一个copyassignmentoperator。

否则,程序反倒会执行得较慢。

2.如果仅仅是为了把NRV优化开关打开而提供一个copyconstructor,那么就没有必要一定要提供一个copyassignmentoperator。

3.copyassignmentoperator有一个非正交情况,那就是它缺乏一个平行于memberinitializationlist的memberassignmentlist。

调用baseclass的copyassignmentoperator示例:

Point:

operator=(p3d);或(*(Point*)this)=p3d;或(Point&)(*this)=p3d;

4.事实上,copyassignmentoperator在虚拟继承情况下行为不佳,需要小心设计和说明。

许多编译器甚至并不尝试取得正确的语意,它们在每一个中间的copyassignmentoperator中调用每一个baseclassinstance,于是造成virtualbasecopyassignmentoperator的多个实体被调用。

建议尽可能不要允许一个virtualbaseclass的拷贝操作,并不要在任何virtualbaseclass中声明datamember。

5.5解构语意学SemanticsofDestruction

如果class没有定义destructor,那么只有在其内带的memberobject或baseclass拥有destructor时,编译器才会自动合成出一个destructor。

一个由程序员定义的destructor被扩展的方式类似constructors被扩展的方式,只是顺序相反:

üdestructor的函数本体首先被执行;

ü如果class拥有memberclassobjects,而后者拥有destructors,那么它们将以声明的相反顺序而调用;

ü如果object内带一个vptr,则现在被重新设定以指向适当baseclass之virtualtable;

ü如果有任何直接的nonvirtualbaseclasses拥有destructor,它们将以声明的相反顺序而调用;

ü如果有任何virtualbaseclasses拥有destructor,而前面讨论的这个class是most-derivedclass,那么它们会以原先构造顺序的相反顺序被调用。

2002-8-19

执行期语意学RuntimeSemantics6.1对象的构造和解构

1.一般而言,constructor和destructor的安插都如你所预期。

但如果一个区段或函数中有一个以上的离开点,情况就会复杂一些,destructor会放在每一个离开点之前。

通常,我们要求将object尽可能放在使用它的那个程序区附近,这样做可以节省不必要的对象产生和销毁操作。

2.C++程序中所有的globalobjects都被放置在程序的datasegment中,如果不明确指定初值,object所配置的内存内容将为0(C并不自动设定初值)。

如果globalobject有constructor和destructor的话,我们说它需要静态的初始化和内存释放操作。

2002-8-203.virtualbaseclass的subobject在每个derivedclass中的位置可能会变动,不能在编译时期确定。

以一个derivedclass的pointer或reference来存取virtualbaseclasssubobject,是一种nonconstantexpression,必须在执行期方可评估求值。

4.使用静态初始化的object有一些缺点。

其一,无法放入try区段,任何throw操作必将触发exceptionhandlinglibrary的默认函数terminate();其二,程序员必须为控制"需要跨越模块做静态初始化"objects的依赖顺序而产生的复杂度付出代价。

建议根本就不要使用那些需要静态初始化的globalobjects。

5.新的C++标准要求编译单位中的staticlocalclassobjects必须在相应函数第一次被调用时才被构造,而且必须以相反的次序销毁。

由于这些objects是在需要时才被

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 考试认证 > 其它考试

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2