第一章:关于对象
P6:C++的布局以及存取时间上主要的额外负担是由virtual引起的,包括virtual function机制以及virtual base class
1.1 C++对象模式
P6:2种数据成员类型:nonstatic和static,三种成员函数类型:static,nonstatic和virtual
P7:简单对象模型:members按照声明顺序,各被指定一个slot,只存放指向member的指针
P8:表格驱动模型:object保存指向两个表格的指针,一个data member table,一个member function member
P9:c++对象模型:nonstatic data member放在每个object之内,static data member放在个别的object之外。static和nonstatic function member也放在个别的object之外。对于virtual function,1:每个class产生一堆指向virtual function的指针,放在表格之中,即virtual table(vtbl)。2:每个object安插一个指针指向相关的virtual table,成为vptr。vptr通常放在第一个slot.
P11:在虚拟继承的情况下,base class不管在继承串链中被派生多少次,永远只会存在一个实例。
P14:VTBL的第一项是type_info for object
1.2 关键词所带来的差异
1.3 对象的差异
P27:一个class object所需内存空间:1:其nonstatic data member的总和大小。2:加上任何由于alignment的需求而填补上去的空间。3:加上为了支持virtual而由内部产生的任何额外负担。
P31:
Bear b;
ZooAnimal *pz=&b;
Bear *pb=&b;
//ZooAnimal 为Bear父类。
//pb所涵盖的地址包含整个bear object,而pz所涵盖的地址只包含Bear object中的ZooAnimal subobject。除了ZooAnimal subobject中出现的members,不能使用pz来直接处理Bear的任何members,唯一的例外是通过virtual机制
P32:类型的信息的封装并不是维护于pz之中,而是维护于link之中,此link存在于object的vptr和vptr所指的virtual table之中。
第二章:构造函数语义学
2.1 Default Constructor 的构造操作
P41:如果一个class没有任何constructor,但它内含一个member object,而后者有constructor,那么这个class的implicit default constructor就是nontrivial。那么编译器就要为class合成一个default constructor。
P41:一个inline函数有静态链接。
P42:被合成default constructor只满足编译器的需要,而不是程序的需要。
P42:若class A内含一个或者一个以上的member class objects,那么A的每一个constructor必须调用每一个member class的default constructor,否则编译器会扩展已存在的constructor,在其中安插一些代码。以member 声明顺序安插程序代码。
P44:如果一个没有constructor的class派生自一个带有default constructor的base class,那么派生类的default constructor会被视为nontrivial,并因此需要被合成。
P44:下面情况也要合成default constructor:1:class声明或继承一个virtual function2:class派生自一个继承串链,其中有一个或更多的virtual base classes。对于此种情况,会有2个扩张行动在编译期间发生:1:一个virtual function table会被编译器产生出来,内放class的virtual functions地址。2:在每个class object中,一个额外的vptr会被编译器合成出来,内含相关class vtbl地址。
P47:在合成的default constructor中,只有base class subobjects和member class objects会被初始化
P47:两个误解(下面两个都是错误的):1:任何class如果没有定义default constructor,就会被合成出一个。2:编译器合成后出来default constructor会显示设定class内每一个data member的默认值。
2.2 Copy Constructor的构造操作
P51:同样,copy constructor区分为trivial和nontrivial,只有nontrivial的实例才会被合成于程序之中。决定是否为trivial的标准在于class是否展现出所谓的bitwise copy semantics。不展现出bitwise copy semantics时才会有编译器生产出来。
P53:什么时候一个class不展现出bitwise copy semantics:1:当class内含一个member object而后者的class声明有一个copy constructor(不论是显示的还是编译器合成的)。2:当class继承一个base class而后者存在一个copy constructor(不论是显示的还是编译器合成的)。3:当class声明一个或多个virtual functions时。4:当class派生自一个继承串链,其中有一个或者多个virtual base classes。
前两种情况,编译器必须将member或者base class的copy constructor调用操作安插到被合成的copy constructor。
P54:当编译器导入一个vptr到class之中时,该class就不展现出bitwise semantics。
2.3 程序转换语义
P61:必要的程序转换有2个阶段:1:重写每一个定义,其中的初始化操作会被剥除。2:class的copy constructor调用操作会被安插进去。
2.4 成员的初始化队伍
P75:下列情况为了让程序能够顺利编译,必须使用member initialization list:1:当初始化一个reference member时。2:当初始化一个const member时。3:当带哦用一个base class的constructor,而它带有一组参数时。4:当调用一个member class的constructor,而它带有一组参数时。
P77:List中的项目顺序是有class中member声明顺序决定的,不是由list中的排列顺序决定。
P79:initialization list的项目被放在用户代码之前。
第三章:DATA语义学
P84:对于空类,有一个隐藏的1byte大小,那是被编译器安插进去的一个char,这使得这个class的两个objects得以在内存中配置独一无二的地址;
P87:一个virtual base class subobject只会在derived class中存在一份实例,不管它在继承体系中出现了多少次。
3.1 Data Member的绑定
3.2 Data Member的布局
3.3 Data Member的存取
P95:每一个static data member只有一个实例,存放在程序的data segment之中。
P96:若取一个static data member的地址,会得到一个指向其数据类型的指针,而不是一个指向其class member的指针。对于static data member,点运算符和->效果一样。
P98:指向data member的指针,其offset值总是被加上1,这样用来区分出“一个指向data member的指针,用以指出class的第一个member”和“一个指向data member的指针,没有指出任何member”。
P99:由于多态的性质,->的类型要到执行期才能确定,会多一个额外的简介导引。而点操作符不会,因为类型就是该类型,不管它是否继承自virtual base class,member的offset位置在编译器就固定了。
3.4 继承与data member
P99:在大部分编译器上头,base class members总是先出现,但属于virtual base class的除外。
P100:一般而言,具体继承并不会增加空间或者存取时间上的额外负担。
P108:virtual table的元素个数一般而言是被声明的virtual function个数,再加上一个或者2个slots(用以支持runtime type identification)
P117:class如果内含一个virtual base class subobjects,将被分割为两部分:一个不变区域和一个共享区域。不变区域总是拥有固定offset,这部分可以直接存取。而共享区域其位置会因为每次的派生操作而有变化,只能间接存取。
3.5 对象成员的效率
P128:程序员如果关心其程序效率,应该实际测试,不要光凭推论、常识判断或者假设。
3.6 指向data members的指针
P130:所有编译器不是把vptr放在对象的头,就是放在对象的尾。
P131:为了区分没有指向任何data member的指针和一个指向第一个data member的指针,当取data members的地址,传回的值总是多1.
P132:取一个nonstatic data member的地址,将会得到它在class中的offset。取一个绑定于真正class object身上的data member的地址,将会得到该member在内存中的真正地址
第四章:function语义学
4.1 member的各种调用方式
P141:nonstatic member function至少必须和一般的nonmember function有相同的效率。
P141:将member function改为nonmember function:1:安插this指针2:改为经由this指针存取3:重新写成一个外部函数。
P150:static member functions的主要特征是没有this指针,所以1:不能直接存取nonstatic members。2:不能声明为const,volatile或者virtual。3:不需要经由class object调用。
4.2 virtual member function
P163:thunk是一小段assembly代码:用来:1:以适当的offset值调整this指针。2:跳到virtual function里。
4.3 函数的效能
P171:inline函数不只能节省一般函数调用所带来的额外负担,也提供了程序优化的额外机会。编译器会将“被视为不变的表达式”提到循环之外。
4.4 指向member function的指针
P176:对于一个virtual member function取地址,只能获得一个virtual table中的索引值。
P181:一个指向member function的指针,是一个结构,含有3个字段:index,faddr,delta。index若不是内含一个相关virtual table的索引值,就是以-1表示函数是nonvirtual,faddr持有nonvirtual member function的地址。delta只有一个可能的this指针调整值。
4.5 inline functions
P188:inline函数中的局部变量,再加上有副作用的参数,可能会导致大量临时性对象的产生。特别是如果它以单一表达式被扩展多次的话。
第五章:构造、析构、拷贝语义学
5.1 无继承情况下的对象构造
P197:c和c++的一个差异在于,BSS data segment在c++中相对的不重要,c++的所有全局对象都被以初始化过的数据来对待。
5.2 继承体系下得对象构造
P211:在虚拟继承情况下,由最底层的class完成共享的基类的构造。
5.3 对象复制语意学
P220:一个class对于默认的copy assignment operator,在以下情况,不会表现出bitwise copy语意:1:单class内含一个member object,而其class 有一个copy assignment operator时。2:当一个class的base class有一个copy assignment operator时。3:单一个class生命了任何virtual functions(我们一定不要拷贝右端class object的vptr地址)。4:当class继承自一个virtual base class。
P222:copy assignment operator缺乏一个member assignment list(也就是平行于member initialization list的东西)
5.4 对象效能
5.5 析构语意学
P231:如果class没有定义destructor,那么只有在class内含的member object(亦或是class自己的base class)拥有destructor的情况下,编译器才会自动合成一个。否则,destructor被视为不需要,也就不会被合成。
P235:析构函数顺序:1:destructor的函数本体首先被执行。2:如果class拥有member class objects,而后者拥有destructors,那么它们会以其声明顺序的相反顺序被调用。3:如果object内含一个vptr,现在被重新设定,指向适当之base class的virtual table。4:如果有任何直接的上一层nonvitrual base classes拥有destructor,它们会以其声明顺序的相反顺序被调用。5:如果有任何virtual base classes拥有destructor,而目前讨论的这个class是最尾端的class,那么它们会以其原来的构造顺序的相反顺序被调用。
第六章:执行期语意学
6.1 对象的构造和析构
P242:c++程序中所有的global objects都被放置在程序的data segment中,如果显示指定给它一个值,此object将以该值为初值,否则所分配到的内存内容为0。
P252:经由一个指针来启动constructor,将无法(不被允许)存取default argument values。
6.2 new 和 delete运算符
6.3 临时性对象
P271:临时对象的被摧毁,应该是对完整表达式求值过程中的最后一个步骤。该完整表达式造成临时对象的产生。
P274:凡持有表达式执行结果的临时性对象,应该存留到object的初始化操作完成为止
P275:如果一个临时性对象被绑定于一个reference,对象将残留,知道被初始化之reference的生命结束,或者直到临时对象的声明范畴结束---视哪一种情况先到达而定。
第七章:站在对象模型的顶端
7.1 Template
P280:template metaprograms技术:class expression templates将在编译时期而非执行期被评估,因为带来重大的效率提升。
P284:只有在member function被使用的时候,才会被实例化
P287:cfront对template的处理是完全解析但不做类型检查;只有在每一个实例化操作发生时才做类型检查。所以在一个parsing策略之下,所有词汇错误和解析错误都会在处理template声明的过程中被标示出来。
P290:Template之中,对于一个nonmember name的决议结果,是根据这个name的使用是否与“用以实例化该template的参数类型”有关而决定的。如果其使用互补相关,那么就以scope of the template declaration来决定name,如果其使用互有关联,那么就以scope of the template instantiation来决定name。