为什么C++可以进行函数重载呢?因为C++对函数进行编译的时候,对函数进行了重命名。即函数名称+参数数目+参数类型=新的函数名称。
对于虚拟成员函数,如果normalize()是一个虚拟成员函数,则以下的调用:
ptr->normalize();
将会被内部转化为: ( *ptr->vptr[1] )( ptr ); //第二个ptr表示this指针。
类似的,如果magnitude()也是一个虚拟函数的话,它将被转换如下:
register float mag = magnitude(); -> register float mag = (*this->vptr[2])(this);
如果明确地调用,将会比较有效率。如下:
register float mag = Point3d::magnitude();
如果magnitude()声明为inline函数会更有效率。
如果是类的对象调用虚拟成员函数,编译器将会对待一般的非静态成员函数一样进行处理,故不支持多态。
如果Point3d::normalize()是一个静态成员函数,那么以下两个调用操作:
obj.normalize(); -> normalize_7Point3dSFv();
ptr->normalize(); -> normalize_7Point3dSFv();
将被转换为一般的nonmember函数调用。
静态成员函数的主要特性就是它没有this指针。
一:它不能够直接存取class中的非静态成员。
二:它不能够被声明为const,volatile 或 virtual。
三:它不需要经由class object 才被调用----虽然大部分时候它是这样被调用的。因此对于
&Point3d::object_count(); 会得到一个数值 unsigned int (*) (); 而不是 unsigned int ( Point3d::*) ();
静态成员函数有一个意想不到的好处:就是成为一个callback函数。
( (Point3d*)0)->object_count(); 其中的object_count() 只是简单的传回_object_count这个静态数据成员。这个习惯的来源是C++在引入静态数据成员之前,C++要求所有的成员函数必须由类的对象来调用。因此希望”没有class object存在“的情况下,程序方法的一个解决之道是很奇特的把0转换成一个class指针,因而提供出一个this指针实体。
object_count( (Point3d*) 0 );
C++中,多态(polymorphism)表示”以一个public base class的指针(或reference),寻址出一个derived class object“的意思。
一个class只会有一个virtual table。每一个table内含其对应的class object中所有的active virtual functions函数实体的地址。这些active virtual functions包括:
一:这个class所定义的函数体。它会改写(overriding)一个可能存在的base class virtual function函数实体。
二:继承自base class的函数实体。这是在derived class决定不改写virtual function时才会出现的情况。
三:一个纯虚拟函数(就是没有定义的函数,如果纯虚拟函数被调用,通常的操作是结束掉这个程序)。
因此,我有这样的式子: ptr->z(); 这个时候,编译器将调用转化为( *ptr->vptr[4])(ptr); 在一个单一继承体系中,virtual function机制的行为十分良好,不但有效率而且很容易塑造出模型来。但是在多重继承和虚拟继承之中,对virtual functions的支持就没有那么美好了。
Base2 *pbase2 = new Derived; 将会转换为 Derived *temp = new Derived; Base2 *pbase2 = temp? temp+sizeof(Base1) : 0;
虚拟继承下的Virtual Functions
class Point2d
{
public:
Point2d( float = 0.0, float = 0.0 );
virtual ~Point2d();
virtual void mumble();
virtual float z();
protected:
float _x, _y;
};
class Point3d : public virtual Point2d
{
public:
Point3d( float = 0.0, float = 0.0, float = 0.0 );
~Point3d();
float z();
protected:
float _z;
};
这个时候的虚拟单一继承并不像”非虚拟的单一继承“情况那样一致。
我的建议是,不要在一个virtual base class 中声明非静态数据成员。如果这么做,你会距离复杂的深渊越来越近。
我们知道,取一个非静态数据成员member的地址,得到的结果是这个member在class布局中的偏移(再加1)。它需要绑定于某个class object的地址上,才能够存取。
取一个非静态成员函数nonstatic member function的地址,如果该函数是nonvirtual,则得到的结果是它在内存中真正的地址。它也需要被绑定于某个class object的地址上,才能够通过它调用该函数。所有的nonstatic member functions都需要对象的地址(以参数this指出)。
使用一个"member function"指针,如果并不用于virtual function, 多重继承,virtual base class等情况的话,并不会比使用一个"nonmember function指针”的成本更高。而virtual function 的出现,会使得"member function指针“更复杂化。
指向Virtual Member Functions的指针
float (Point::*pmf)() = &Point::z;
Point *ptr = new Point3d;
pmf, 一个指向member function的指针,被设置为Point::z()(一个virtual function)的地址。ptr->z(), 被调用的是Point3d::z()。我们从pmf间接调用z()呢?(ptr->pmf)(); 调用的是Point3d.z()吗?答案是yes。问题时如何实现的呢?
我们知道,对一个非静态数据成员取其地址,将获得该函数在内存中的地址,然而面对一个虚拟函数,其地址在编译时候是未知的,所能知道的仅是virtual function在其相关的virtual table中的索引值。也就是说,对一个virtual member function取其地址,所能获得的只是一个索引值。例如:
class Point
{
public:
virtual ~Point();
float x();
float y();
virtual float z();
};
&Point::~Point = 1; 取&Point::x()的地址则是内存中的地址。通过pmf来调用z(),会被转化为一个编译时期的式子,一般形式如下所示:
(*ptr->vptr[ (int)pmf ] ) (ptr);
Inline Functions
一般而言,处理一个inline函数,有两个阶段:
一:分析函数定义。如果函数因其复杂度,或因其建构问题,被判断不可成为inline,它会被转为一个static函数。
二:真正的inline函数扩展操作是在调用的那一个点上。这会带来参数的求值操作(evaluation)以及临时性对象的管理。
在inline函数的扩展进行处理时,大部分的编译器厂商似乎认为不值得在inline支持技术上做详细的讨论。我们只有进入到汇编代码中,才可以看到是否真正的实现了Inline。
一般而言,inline函数中的每一个局部变量都必须被放在函数调用的一个封闭区段内,拥有一个独一无二的名字。Inline函数中的局部变量,再加上有副作用的参数,可能会导致大量临时性对象的产生。特别是如果它以单一表达式被扩展多次的话。如果一个Inline函数被调用多次的话,会产生大量的扩张码,使程序的大小暴涨。所以,我们需要小心的处理。