***非静态成员函数(Nonstatic Member Functions)***
C++的设计准则之一就是:nonstatic member function至少必须和一般的nonmember function有相同的效率。也就是说,如果我们要在以下两个函数之间作选择:
 
 
  
float 
   magnitude3d( 
  const 
   Point3d  
  * 
  this 
  )  
  ... 
  { ... } 
  
 
  float 
   Point3d::magnitude3d()  
  const 
    
  ... 
  { ... } 
那么选择member function不应该带来什么额外负担。因为编译器内部已将“member函数实体”转化为对等的“nonmember函数实体”。下面是magnitude()的一个nonmember定义:
 
   
float    Pointer3d::magnitude()  
  const 
  
   ... 
  {
return sqrt(_x*_x + _y*_y + _z*_z);
}   
   // 
   内部转化为 
  
   float 
   magnitude_7Point3dFv( 
  const 
   Point3d  
  * 
  this 
  )   
  // 
  已对函数名称进行“mangling”处理 
  
   ... 
  {
return sqrt(this->_x*this->_x + this->_y*this->_y + this->_z*this->_z);
} 
现在,对该函数的每一个调用操作也都必须转换:
 
   
obj.magnitude();
   //    转换为 
  
   magnitude_7Point3dFv(   & 
  obj); 
对于class中的memeber,只需在member的名称中加上class名称,即可形成独一无二的命名。但由于member function可以被重载化,所以需要更广泛的mangling手法,以提供绝对独一无二的名称。其中一种做法就是将它们的参数链表中各参数的类型也编码进去。
 
   
class    Point    ...   {
public:
void x(float newX);
float x();
...
}   ;
   //    内部转化为   
   class    Point    ... 
  {
void x_5PointFf(float newX);  // F表示function,f表示其第一个参数类型是float
float x_5PointFv();  // v表示其没有参数
}   ; 
上述的mangling手法可在链接时期检查出任何不正确的调用操作,但由于编码时未考虑返回类型,故如果返回类型声明错误,就无法检查出来。
 
***虚拟成员函数(Virtual Member Functions)***
对于那些不支持多态的对象,经由一个class object调用一个virtual function,这种操作应该总是被编译器像对待一般的nonstatic member function一样地加以决议:
 
   
//    Point3d obj   
   obj.normalize();
   //    不会转化为   
   (   *   obj.vptr[   1 
  ])( 
  & 
  obj);
   //    而会被转化未   
   normalize_7Point3dFv(   &   obj); 
 
***静态成员函数(Static Member Functions)***
在引入static member functions之前,C++要求所有的member functions都必须经由该class的object来调用。而实际上,如果没有任何一个nonstatic data members被直接存取,事实上就没有必要通过一个class object来调用一个member function。
这样一来便产生了一个矛盾:一方面,将static data member声明为nonpublic是一种好的习惯,但这也要求其必须提供一个或多个member functions来存取该member;另一方面,虽然你可以不靠class object来存取一个static member,但其存取函数却得绑定于class object之上。static member functions正是在这种情形下应运而生的。
编译器的开发者针对static member functions,分别从编译层面和语言层面对其进行了支持:
(1)编译层面:当class设计者希望支持“没有class object存在”的情况时,可把0强制转型为一个class指针,因而提供出一个this指针实体: 
   
//    函数调用的内部转换   
   object_count((Point3d   *   )   0   ); 
(2)语言层面:static member function的最大特点是没有this指针,如果取一个static member function的地址,获得的将是其在内存中的位置,其地址类型并不是一个“指向class member function的指针”,而是一个“nonmember函数指针”:
 
   
unsigned    int    Point3d::object_count()    ...   { return _object_count; }   
   &   Point3d::object_count();
   //    会得到一个地址,其类型不是   
   unsigned    int    (Point3d::   *   )();
   //    而是   
   unsigned    int    (   *   )(); 
static member function经常被用作回调(callback)函数。
 
***虚拟成员函数(Virtual Member Functions)***
对于像ptr->z()的调用操作将需要ptr在执行期的某些相关信息,为了使得其能在执行期顺利高效地找到并调用z()的适当实体,我们考虑往对象中添加一些额外信息。
(1)一个字符串或数字,表示class的类型;
(2)一个指针,指向某表格,表格中带有程序的virtual functions的执行期地址;
在C++中,virtual functions可在编译时期获知,由于程序执行时,表格的大小和内容都不会改变,所以该表格的建构和存取皆可由编译器完全掌握,不需要执行期的任何介入。
(3)为了找到表格,每一个class object被安插上一个由编译器内部产生的指针,指向该表格;
(4)为了找到函数地址,每一个virtual function被指派一个表格索引值。
一个class只会有一个virtual table,其中内含其对应的class object中所有active virtual functions函数实体的地址,具体包括:(a)这个class所定义的函数实体
它会改写一个可能存在的base class virtual function函数实体。若base class中不存在相应的函数,则会在derived class的virtual table增加相应的slot。
(b)继承自base class的函数实体
这是在derived class决定不改写virtual function时才会出现的情况。具体来说,base class中的函数实体的地址会被拷贝到derived class的virtual table相对应的slot之中。
(c)pure_virtual_called函数实体
对于这样的式子: 
ptr   ->   z();
 
运用了上述手法后,虽然我不知道哪一个z()函数实体会被调用,但却知道每一个z()函数都被放在slot 4(这里假设base class中z()是第四个声明的virtual function)。
 
   
//    内部转化为   
   (   *   ptr   ->   vptr[   4   ])(ptr); 
 
***多重继承下的Virtual Functions***
在多重继承中支持virtual functions,其复杂度围绕在第二个及后继的base classes身上,以及“必须在执行期调整this指针”这一点。
多重继承到来的问题:
(1)经由指向“第二或后继之base class”的指针(或reference)来调用derived class virtual function,该调用操作连带的“必要的this指针调整”操作,必须在执行期完成;以下面的继承体系为例:
 
   
class    Base1    ...   {
public:
Base1();
virtual ~Base1();
virtual void speakClearly();
virtual Base1 *clone() const;
protected:
float data_Base1;
}   ;

   class    Base2    ...   {
public:
Base2();
virtual ~Base2();
virtual void mumble();
virtual Base2 *clone() const;
protected:
float data_Base2;
}   ;

   class    Derived :    public    Base1,    public    Base2    ...   {
public:
Derived();
virtual ~Derived();
virtual Derived *clone() const;
protected:
float data_Derived;
}   ; 对于下面一行: 
    
Base2     *    pbase2     =         new     Derived; 
会被内部转化为:
      
//     转移以支持第二个base class    
    Derived     *    temp     =         new     Derived;
Base2     *    pbase2     =     temp     ?     temp     +         sizeof    (Base1) :  
   0 
   ; 
如果没有这样的调整,指针的任何“非多态运用”都将失败:
      
pbase2->data_Base2; 
当程序员要删除pbase2所指的对象时:
      
//     必须调用正确的virtual destructor函数实体
    //     pbase2需要调整,以指出完整对象的起始点    
    delete pbase2; 
指针必须被再一次调整,以求再一次指向Derived对象的起始处。然而上述的offset加法却不能够在编译时期直接设定,因为pbase2所指的真正对象只有在执行期才能确定。
 自此,我们明白了在多重继承下所面临的独特问题:经由指向“第二或后继之base class”的指针(或reference)来调用derived class virtual function,该调用操作所连带的“必要的this指针调整”操作,必须在执行期完成。有两种方法来解决这个问题:
(a)将virtual table加大,每一个virtual table slot不再只是一个指针,而是一个聚合体,内含可能的offset以及地址。这样一来,virtual function的调用操作发生改变:      
(    *    pbase2    ->    vptr[    1    ])(pbase2);
    //     改变为    
    (    *    pbase2    ->    vptr[    1    ].faddr)(pbase2     +     pbase2    ->    vptr[    1    ].offset); 
这个做法的缺点是,它相当于连带处罚了所有的virtual function调用操作,不管它们是否需要offset的调整。
(b)利用所谓的thunk(一小段assembly码),其做了以下两方面工作:(1)以适当的offset值调整this指针;(2)跳到virtual function去。
      
pbase2_dtor_thunk:
    this         +=         sizeof    (base1);
Derived::    ~    Derived(    this    ); 
Thunk技术允许virtual table slot继续内含一个简单的指针,slot中的地址可以直接指向virtual function,也可以指向一个相关的thunk。于是,对于那些不需要调整this指针的virtual function而言,也就不需要承载效率上的额外负担。
(2)由于两种不同的可能:(a)经由derived class(或第一个base class)调用;(b)经由第二个(或其后继)base class调用,同一函数在virtual table中可能需要多笔对应的slot;
      
Base1     *    pbase1     =         new     Derived;
Base2     *    pbase2     =         new     Derived;

delete pbase1;
delete pbase2; 
虽然两个delete操作导致相同的Derived destructor,但它们需要两个不同的virtual table slots:
(a)pbase1不需要调整this指针,其virtual table slot需放置真正的destructor地址
(b)pbase2需要调整this指针,其virtual table slot需要相关的thunk地址
具体的解决方法是:
在多重继承下,一个derived class内含n-1个额外的virtual tables,n表示其上一层base classes的数目。按此手法,Derived将内含以下两个tables:vtbl_Derived和vtbl_Base2_Derived。(3)允许一个virtual function的返回值类型有所变化,可能是base type,可能是publicly derived type,这一点可以通过Derived::clone()函数实体来说明。
      
Base2     *    pb1     =         new     Derived;

    //     调用Derived::clone()
    //     返回值必须被调整,以指向Base2 subobject    
    Base2     *    pb2     =     pb1    ->    clone(); 
当运行pb1->clone()时,pb1会被调整指向Derived对象的起始地址,于是clone()的Derived版会被调用:它会传回一个指针,指向一个新的Derived对象;该对象的地址在被指定给pb2之前,必须先经过调整,以指向Base2 subobject。
当函数被认为“足够小”的时候,Sun编译器会提供一个所谓的“split functions”技术:以相同算法产生出两个函数,其中第二个在返回之前,为指针加上必要的offset,于是无论通过Base1指针或Derived指针调用函数,都不需要调整返回值;而通过Base2指针所调用的,是另一个函数。 
***虚拟继承下的Virtual Functions***
其内部机制实在太过诡异迷离,故在此略过。唯一的建议是:不要在一个virtual base class中声明nonstatic data members。
 
***函数的效能***
由于nonmember、static member和nonstatic member函数都被转化为完全相同的形式,故三者的效率安全相同。virtual member的效率明显低于前三者,其原因有两个方面:(a)构造函数中对vptr的设定操作;(b)偏移差值模型。
 
***指向Member Function的指针***
取一个nonstatic member function的地址,如果该函数是nonvirtual,则得到的结果是它在内存中真正的地址。
我们可以这样定义并初始化该指针:      
double     (Point::    *    coord)()     =         &    Point::x; 
想调用它,可以这么做:
      
(origin.*coord)();   
    (ptr->*coord)();
 
“指向Virtual Member Functions”之指针将会带来新的问题,请注意下面的程序片段:
      
float     (Point::    *    pmf)()     =         &    Point::z;
Point     *    ptr     =         new     Point3d; 
其中,pmf是一个指向member function的指针,被设值为Point::z()(一个virtual function)的地址,ptr则被指向一个Point3d对象。
如果我们直接经由ptr调用z():      
ptr    ->    z();      //     调用的是Point3d::z() 
但如果我们经由pmf间接调用z():
      
(ptr    ->*    pmf)();      //     仍然调用的是Point3d::z() 
也就是说,虚拟机制仍然能够在使用“指向member function之指针”的情况下运行,但问题是如何实现呢?
对一个nonstatic member function取其地址,将获得该函数在内存中的地址;而对一个virtual member function取其地址,所能获得的只是virtual function在其相关之virtual table中的索引值。因此通过pmf来调用z(),会被内部转化为以下形式:  
(*ptr->vptr[(int)pmf])(ptr); 但是我们如何来判断传给pmf的函数指针指向的是内存地址还是virtual table中的索引值呢?例如以下两个函数都可指定给pmf:      
//     二者都可以指定给pmf    
    float     Point::x()     ...    { return _x; }          //     nonvirtual函数,代表内存地址    
    float     Point::z()     ...    { return 0; }          //     virtual函数,代表virtual table中的索引值 
cfront 2.0是通过判断该值的大小进行判断的(这种实现技巧必须假设继承体系中最多只有128个virtual functions)。
为了让指向member functions的指针也能够支持多重继承和虚拟继承,Stroustrup设计了下面一个结构体:      
//     用以支持在多重继承之下指向member functions的指针    
    struct     _mptr     ...    {
int delta;
int index;
union ...{
ptrtofunc faddr;
int v_offset;
};
}    ; 
其中,index表示virtual table索引,faddr表示nonvirtual member function地址(当index不指向virtual table时,被设为-1)。
在该模型之下,以下调用操作会被转化为:      
(ptr    ->*    pmf)();
    //     内部转化为    
    (pmf.index     <         0    )
    ?     (    *    pmf.faddr)(ptr)      //     nonvirtual invocation    
    : (    *    ptr    ->    vptr[pmf.index](ptr)      //     virtual invocation 
对于如下的函数调用:
      
(pA.    *    pmf)(pB);      //     pA、pB均是Point3d对象 
会被转化成:
      
pmf.iindex     <         0    
    ?     (    *    pmf.faddr)(    &    pA     +     pmf.delta, pB)
: (    *    pA._vptr_Point3d[pmf.index].faddr)(    &    pA     +     pA._vptr_Point3d[pmf.index]     +     delta, pB); 
 
***Inline Functions***
在inline扩展期间,每一个形式参数都会被对应的实际参数取代。但是需要注意的是,这种取代并不是简单的一一取代(因为这将导致对于实际参数的多次求值操作),而通常都需要引入临时性对象。换句话说,如果实际参数是一个常量表达式,我们可以在替换之前先完成其求值操作;后继的inline替换,就可以把常量直接绑上去。
举个例子,假设我们有以下简单的inline函数:      
inline     int     min(    int     i,     int     j)
    ...    {
return i < j ? i : j;
} 
对于以下三个inline函数调用:
      
minval     =     min(val1,val2);
minval     =     min(    1024    ,    2048    );
minval     =     min(foo(),bar()    +    1    ); 
会分别被扩展为:
      
minval     =     val1     <     val2     ?     val1 : val2;      //     参数直接代换    
    minval     =         1024    ;      //     代换之后,直接使用常量    
    int     t1;
    int     t2;
minval     =     (t1     =     foo()), (t2     =     bar()    +    1    ),t1     <     t2     ?     t1 : t2;      //    有副作用,所以导入临时对象 
inline函数中的局部变量,也会导致大量临时性对象的产生。
      
inline     int     min(    int     i,     int     j)
    ...    {
int minval = i < j ? i : j;
return minval;
} 
则以下表达式:
      
minval     =     min(val1, val2); 
将被转化为:
      
int     _min_lv_minval;
minval     =     (_min_lv_minval     =     val1     <     val2     ?     val1 : val2),_min_lv_minval; 总而言之,inline函数中的局部变量,再加上有副作用的参数,可能会导致大量临时性对象的产生。特别是如果它以单一表达式被扩展多次的话。新的Derived对象的地址必须调整,以指向其Base2 subobject。