草庐IT

c++ 对象模型 函数

chenglixue 2023-04-19 原文

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因其连锁复杂度而没办法展开

有关c++ 对象模型 函数的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  4. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  5. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  6. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  7. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  8. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  9. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  10. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

随机推荐