第一章 关于对象

加上封装后的布局成本

&nbsp&nbsp&nbsp&nbspC++在布局以及存取时间上主要的额外负担是由virtual引起的,包括:

  • virtual function 机制用以支持一个有效率的“执行期绑定”。
  • virtual base class 用以实现“多次出现在集成体系中的base class,有一个单一而被共享的实例”。

C++对象模型

&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbspStroustrup当初设计的C++对象模型是从简单对象模型派生而来的,并对内存空间和存取时间做了优化。在此模型中,Nonstatic data members被配置于每一个class object之内,static data members 则被配置于个别的class object之外。static和nonstatic function也被放在个别的class object之外。Virtual function则以两个步骤支持之:

  • 每一个class产生出一堆只想virtual functions的指针,放在表格之中。这个表格被称为virtual table(vtbl)。
  • 每一个class object被安插一个指针,指向相关的virtual table。通常这个指针被称为vptr。vptr的设定和重置都由每一个class的constructor、destructor、和copy assignment运算符自动完成。每一个class所关联的type_info object(用以支持runtime type identification,RTTI)也经由virtual table 被指出来,通常放在表格的第一个slot。

对象的差异

&nbsp&nbsp&nbsp&nbsp什么是程序设计范式?
&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp一种环境设计和方法论的模型或范例:系统和软件以此模型来开发和运行。一个现役的范式可能会有数个开发中的替代范式。以下是一些大家比较熟悉的范式:函数化程序设计、逻辑程序设计、几何计算、数值计算、面向对象设计。

&nbsp&nbsp&nbsp&nbspC++程序设计模型直接支持三种程序设计范式

  • 程序模型(procedural model)。就像C一样,C++当然也支持它,字符串的处理就是一个例子,我们可以使用字符数组以及str*函数族群(地应在标准的C标注库中):
    char boy[]="Danny";
    char *p_son;
    ...
    p_son = new char [strlen(boy)+1];
    strcpy(p_son,boy);
    ...
    if(!strcmp(p_son,boy))
      take_to_disneyland(boy);
  • 抽象数据类型模型(abstract data type model,ADT)。此模型所谓的“抽象”是和一组表达式(public 接口)一起提供的,那时期运算定义仍然隐而未名。例如下面的string class:
    string girl = "Anna";
    string daughter;
    ...
    //string :: operator=();
    daughter = girl;
    ...
    //string::operator==();
    if (girl==daughter)
        take_to_disneyland(girl);
  • 面向对象模型(object-oriented model)。在此模型中有一些彼此相关的类型,通过一个抽象的base class(用以提供共同接口)被封装起来。
    void check_in( Library_materials *pmat){
        if (pmat->late())
            pmat->fine();
        pmat->check_in();
        if (Lender *plend = pmat->reserved() )
            pmat->notify(plend);
    }

&nbsp&nbsp&nbsp&nbsp纯粹以一种范式写程序,有助于整体行为的良好稳固。然而如果混合了不同的范式,就可能会带来让人吃惊的后果,特别是在没有谨慎处理的情况下。

&nbsp&nbsp&nbsp&nbspc++以下列方法支持多态:

  • 经由一组隐式的转化操作。例如把一个derived class指针转化为一个指向其public base type 的指针:
shape *ps = new circle(); 
  • 经由virtual function 机制:
ps->rotate();  
  • 经由dynamic_cast 和 type_id 运算符:
if(circle *pc= dynamic_cast< circle* >(ps))...

多态的主要用途式经由一个共同的接口来影响类型的封装,这个接口通常被定义在一个抽象的base class种。

&nbsp&nbsp&nbsp&nbsp需要多少内存才能够表现一个class object?一般而言要有:

  • 其nonstatic data members 的总和大小。
  • 加上任何由于 alignment 的需求而填补上去的空间。
  • 加上为了支持virtual 而由内部产生的任何额外负担。

第二章 构造函数语意学

何为 trivial constructor ?

&nbsp&nbsp&nbsp&nbsp对于class X,如果没有任何user-declared constructor,那么会有一个default constructor 被隐式声明出来... ...一个被隐式声明出来的default constructor 将是一个trivial(浅薄而无能,没啥用的)constructor... ...
&nbsp&nbsp&nbsp&nbsp C++ standard 然后开始一一叙述什么情况下这个implicit default constructor 会被视为trivial 。一个nontrivial default constructor 在arm的术语中就是编译器所需要的那种,必要的话会由编译器合成出来。下面4小节分别讨论nontrivial default constructor的四种情况:

1.“带有default constructor”的Member Class Object

&nbsp&nbsp&nbsp&nbsp如果一个class没有任何constructor,但它内含一个member object,而后者有default constructor,那么这个class 的implicit default constructor 就是“nontrivial”,编译器需要为该class合成出一个default constructor。不过这个合成操作只有在constructor 真正需要被调用时才会发生。

2.“带有default constructor” 的base class

&nbsp&nbsp&nbsp&nbsp类似的道理,如果一个没有任何constructors的class派生自一个“带有default constructor”的 base class,那么这个 derived class 的default constructor 会被视为nontrivial,并因此需要被合成出来。它将调用上一层base classes的default constructor (根据他们的声明顺序)。对一个后继派生的class而言,这个合成的constructor 和一个“被显示调用的default constructor”没有什么差异。

3.“带有一个virtual function”的class

&nbsp&nbsp&nbsp&nbsp另有两种情况,也需要合成出default constructor:

  • class 声明(或继承)一个virtual function
  • class 派生自一个继承串链,其中有一个或更多的virtual base classes。

4.“带有一个virtual base class”的class

虚基类的实现法在不同的编译器间有极大的差异。然而,每一种实现法的共同点在于必须使虚基类在其每一个派生类中的位置,能够于执行期准备妥当。

void foo(const A* pa){pa->__vbcX->i =1024; }

&nbsp&nbsp&nbsp&nbsp其中__vbcX表示编译器所产生的指针,指向virtual base class X。
&nbsp&nbsp&nbsp&nbsp正如你所揣测的那样,__vbcX(或编译器所做出的某个什么东西)是在class object 构造期间被完成的。对于 class 所定义的每一个 constructor,编译器会安插那些“允许每一个virtual base class 的执行期存取操作” 的代码。如果class没有声明任何constructor,编译器必须为它合成一个default constructor。

总结

&nbsp&nbsp&nbsp&nbsp有四种情况,会造成“编译器必须为未声明constructor的classes合成一个default constructor”。C++ Standard 把那些合成物称为 implicit nontrivial default constructors。被合成出来的constructor 只能满足编译器(而非程序)的需要。它之所以能够完成任务,是借着“调用member object 或 base class的default constructor”或是“为每一个object初始化其 virtual function 机制或virtual base class机制”而完成的。至于没有存在那四种情况而又没有声明任何constructor 的 classes,我们说它们拥有的是implicit trivial default constructors,它们实际上并不会被合成出来。
&nbsp&nbsp&nbsp&nbsp在合成的default constructor中,只有base class subobjects 和member class objects 会被初始化。所有其他的nonstatic data member(如整数、整数指针、整数数组等等)都不会被初始化。这些初始化操作对程序员而言或许有需要,但对编译器则非必要。如果程序需要一个“把某指针设为0”的default constructor,那么提供它的人应该是程序员。
&nbsp&nbsp&nbsp&nbspc++新手一般有两个常见的误解:

  • 任何class如果没有定义default constructor,就会被合成出一个来。
  • 编译器合成出来的default constructor 会显式设定“class内每一个data member的默认值”。

如你所见,没有一个是正确的。

不要bitwise copy semantics!

&nbsp&nbsp&nbsp&nbsp什么时候一个class不展现出“bitwise copy semantics”呢?有四种情况:

  • 当class 内含一个 member object 而后者的class 声明有一个copy constructor时。
  • 当class 继承自一个base class 而后者存在一个copy constructor时(再次强调,不论是被显式声明或是被合成而得)。
  • 当class 声明了一个或多个virtual functions时。
  • 当class 派生自一个继承串链,其中有一个或多个virtual base classes时。

本文固定链接: http://www.js-code.com/cpp/cpp_60325.html