c++ 对象模型 函数

member functions的调用方式

  • c++支持三种类型的member functions:static、nonstatic、virtual,且每一种调用方式不尽相同

nonstatic member functions

  • nonstatic member function至少和nonmember function有着相同的效率

​ 现有如下函数调用:

float do(const A *_this) {...}
float A::do() const {...}

//第一个函数转换
float do(const A *_this)
{
    
    return sqrt(
    	_this->x * _this->x + _this->y * _this->y + _this->z * _this->z
    )
        
};

//第二个nonstatic member function转换成上面相同的形式nonmember function
  • nonstatic member function转化为 nonmember function步骤

    • 改写函数原型以安插一个额外参数到member function,将提供class object存取渠道,此参数也就是this指针。若member function为const,this也需加个const

      float do(const A* const _this)
      
    • 将每个对nonstatic data member的存取操作改为this指针

      _this->x * _this->x + _this->y * _this->y + _this->z * _this->z
      
    • 将member function经过mangling处理重新写为一个外部函数,且名称是独一无二的

      extern do_7AFv(  A* const this );
      
  • 一般而言,data member的名称前会加上class名称,形成独一无二的命名;而member function则还需加上参数链表

    class B { public: int val; ... };
    
    //对val进行name mangling
    val_3;
    
    -----------------------
        
    class C
    {
        public:
        	void x(float newX);
        	float x();
    }
    
    //member function进行name mangling
    void x__5CFf(float newX);
    float x__5CFv();
    

virtual member functions

​ 现有如下代码:

class A
{
    virtual A do1() const;
    virtual float do2() const;
}

A a;
A* pt = &a;

pt->do1();
//转化
( *pt->vptr[1] )(pt);

float d = do2();
//转化
float d = ( *this->vptr[2] )( this );

a.do1();
//转化
( *a.vptr[1] )( &a ); 

​ ( * pt -> vptr [1] )( pt )其中:1为virtual table slot的索引值,关联到virtual member function,也就是do1()

​ ( *a.vptr[1] )( &a ) 没有必要,应该这样调用:A::do1()

  • 经由class object调用virtual function优化跟nonstatic member function一样

  • 为支持virtual function机制,需要能对多态对象进行执行期类型判断,将必要信息加在指针或引用上。而必要信息则有:

    • 指针或引用指向的对象的地址
    • 对象类型的结构的地址
  • 多态表示用一个public base class的指针或引用寻址一个derived class object

    Point* ptr;
    ptr = new Point2d;
    
    • 当前ptr被称为消极的多态形式,可以在编译时期完成(virtual base class除外);当ptr指出的具体对象被使用时才是积极

      ptr->z();
      
    • class是否支持多态,唯一方法是看其是否有virtual function

  • 实现多态。我们需要在每个class object上增加两个members:

    • 一个字符串或数字,表示class类型
    • 一个指针,指向一表格,表格中含有virtual functions执行期地址
  • 随后只需两步即可找到其地址:

    • 每个class object安插一个由编译器生成的指针,该指针指向表格
    • 每个virtual function被指派一个表格索引值
  • 一个class只有一个virtual table,每个table内含对应的class object中的active virtual functions实例地址。而active virtual functions又包括:

    • 这一class定义的函数实例
    • 继承自base class的函数实例
    • 一个pure virtual called函数
  • 对于多重继承,销毁对象时若调用delete需要指向derived class object的起始处,但因为指针或引用真正所指的对象在执行期才可以确定,所以offset无法在编译期求得。针对多重继承这种情况,derived class需要内含n-1个额外的virtual tables(n表示其上一层base classes个数)。那么如何支持一个class拥有多个virtual tables呢?只需将每一个tables以外部对象的形式产出,并赋予独一无二的名称

  • 不要在virtual base class中声明nonstatic data members

static member function

  • static member function特性
    • 没有this指针,成为一个callback函数
    • 不可直接存取class中的nonstatic members
    • 不可被声明为const、volatile、virtual
    • 不需要经由class object调用
  • static member function同样也有name mangling
  • 对static member function取地址,得到的是内存中的地址;由于没有this指针,static member function地址类型是一个nonmember函数指针

指向member functions 的指针

  • 指向member function的指针和指向member selection operator的指针,其作用是作为this指针的空间保留者。这也说明了为什么static member function的指针类型是函数指针,毕竟其没有this指针

  • 使用member function指针,若不用于virtual function、多重virtual继承、virtual base class,其成本跟用nomember function指针差不多

  • virtual member function的地址在编译期是未知的,我们所能知道的仅是virtual function在其相关之virtual table的索引值

inline functions

  • inline关键词只是一个请求,若此请求被编译期接受,编译期则认为其可以用一个表达式将函数展开

  • inline函数的复杂度通过计算assignments、function calls、virtual function calls等操作的次数以及每个表达式种类的权值综合决定

  • 若函数因其复杂度或建构问题,被判断不可称为Inline,那么此函数将被转换为static函数,并在"被编译模块"内产生对应的函数定义

  • inline function展开期间,做了以下三件事:

    • 每个形参都被对应的实参取代。但这其中可能会导致实际参数的多次求值,面对这种情况,需要引入临时对象。比如,若实际参数是常量表达式,在替换前先引入临时对象,常量表达式求值后赋值给临时对象,后继inline替换只需使用临时对象

      inline int min( int i, int j )
      {
          return i < j ? i : j;
      }
      
      inline int bar()
      {
          int minval;
          
          minval = min( foo(), bar() + 1 );
          
          return minval;
      }
      
      //minval = min( foo(), bar() + 1 )展开
      int t1, 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函数提供强有力的工具,但于noninline函数相比还是需要更小心地处理

  • 尽量不要Inline中套inline,可能会使简单的Inline因其连锁复杂度而没办法展开

原文链接: https://www.cnblogs.com/chenglixue/p/16814137.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    c++ 对象模型 函数

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/5366

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月4日 下午7:33
下一篇 2023年2月4日 下午7:33

相关推荐