深度探索C++对象模型 Function语意学笔记

    为什么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函数被调用多次的话,会产生大量的扩张码,使程序的大小暴涨。所以,我们需要小心的处理。

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值