c++对象模型研究4:函数

成员的各种调用方式

一路走来,virtual函数貌似是用函数指针实现。或许虚函数掌握起来比函数指针要简单很多。貌似虚函数的性能要略低于函数指针。

以我的理解,c偏向于算法设计,c++偏向于业务。


非静态成员函数

c++的设计准则之一就是:非static成员函数 要与一般的非成员函数有相同的效率。非static成员函数其实都是最后被编译器转化成非成员函数的。


转化步骤

1.改写函数的signature(函数原型)以安插一个额外参数(this指针)到成员函数中。

2.将每一个对非static成员数据的存取操作改为由this指针存取

ps:this其实就是类对象的地址

3.将成员函数重新写成一个外部函数,并将函数名称经过magling(所谓的Name Mangling是指在member的名称上加上class名称以及成员函数的signature)处理,使它成为在程序中独一无二的语汇。


调用

obj. magnitude ();//变为 magnitude_7Point3dFv(&obj);
ptr-> magnitude();//变为 magnitude_7Point3dFv(ptr);


虚拟成员函数

obj. magnitude ();//变为 magnitude_7Point3dFv(&obj);
ptr-> magnitude();//变为 * ptr -> vptr[ 1 ] )( ptr )

对于 ptr -> magnitude( ); 而言,其内部转换为( * ptr -> vptr[ 1 ] )( ptr );,(理由是:指针支持多态,该指针指向的到底是谁的函数还不明确,所以一定得查虚表)。之前我写过一指向函数的指针数组,原理也差不多。

对于obj.magnitude( ); 而言,由于只有指针和引用才能支持多态,一般的object并不支持多态,所以调用上会被编译器像对待一般非static成员函数一样。


静态成员函数

如果normalize()是一个static成员函数,以下两个调用:

obj.normalize();
ptr->normalize();

将被转换为一般的非成员函数调用,像这样:

normalize_7Point3dSFv();//SFv表示static member function,拥有一个空白(void)的参数链表(argument list)
normalize_7Point3dSFv();

独立于类对象之外的存取类对象的static成员数据操作主要有两种方法

1.未引入static成员函数概念之前,程序上的解决之道是将0强制转换为一个类指针,因而提供一个this指针实例。

2.再就是引入了static成员函数的概念,用static成员函数去访问static成员数据。

static成员函数的主要特性就是它没有this指针,所以1.他不能直接存取非static成员数据;2.它不需要经由类对象才能被调动;3.他不能被声明为const、volatile或virtual。

再就是由于static成员函数由于不存在this指针,所以它的地址类型不是一个“类成员函数指针”而是一个“非成员函数指针”。(this指针是指向隐含的对象的,对象中仅仅存储了非static成员数据和指向虚函数表的vptr。所以访问了非static成员数据的非static成员函数需要this指针,访问虚函数表的virtual member function也需要this指针)

虚拟成员函数

Virtual function的一般实现模型(无继承):每一个class有一个virtual table,内含class之中有作用的virtual function的地址,然后每个object有一个vptr,指向virtual table的存在。

单继承下的virtual function

对于单继承的派生类而言,它只有一张virtual table


对于派生类而言

1.它可以继承基类所声明的virtual function,具体地说,父类的virtual function的实体地址会被拷贝到子类的virtual table中去。

2.它可以拥有自己的virtual function,这表示它自己的virtual function实体地址必须拷贝到自己的virtual table中去。

3.它可以改写父类的virtual function,这表示,该改写后的virtual function的实体地址需要替换原来父类虚函数实体地址在自己的virtual table的位置。


多重继承下的virtual function

对于多重继承下的派生类而言,它有n-1张virtual table。

在多重继承中支持virtual function,其复杂度围绕在第二以及后继的基类上。 “ 必须在执行期调整this指针 ”。

然而上述问题的offset加法却不能够在编译时期直接设定,因为pbase2所指的真正对象只有在执行期才能确定。所以必须在执行期调整this指针。

为解决这个问题,一帮子人想了一种办法Thunk技术

所谓thunk是一小段assembly代码,用来(1) 以适当的offset值调整this指针(2)跳到virtual function去。例如,经由一个Base2执行调用Derived destructor。

Thunk技术允许virtual table slot继续内含一个简单的指针,因此多重继承不需要任何空间上的额外负担。Slots中的地址可以直接指向virtual function,也可以指向一个相关的thunk(如果需要调整this指针的话)。于是,对于那些不需要调整this指针的virtual function而言,也就不需承载效率上的额外负担。


在多重继承下一个derived class内含n-1个额外的virtual table。如《关于对象》中的描述,derived class 与最左端基类共享一张virtual table作为主要表格,其他基类各有一张virtual table作为次要表格。


虚继承下的virtual functions
在虚基类中最好不要声明非static成员数据。

函数的效能



如上面所讨论的,nonmember或static member或nonostatic member函数都被转化为完全相同的形式。所以我们毫不惊讶地看到三者的效率完全相同。

此例显示,inline函数不只能够节省一般函数调用所带来的额外负担,也提供程序优化的额外机会。

 virtual member涉及的操作太多,所以效能不高。


指向成员函数的指针

在《数据》中,我们已经看到了,取一个非static成员数据的地址,得到折结果是该成员在类布局中的bytes位置(再加1)。你可以想象它是一个不完整的值,它需要被绑定于某个类对象的地址上,才能够被存取。

取一个非static成员函数的地址,如果该函数是非virtual,则得到的结果是它在内存中真正的地址。然而这个值也是不完全的,它也需要被绑定于某个类对象的地址上,才能够通过它调用该函数。所有的非static成员函数都需要对象的地址(以参数this指出)。

指向成员函数的指针的声明语法,以及指向“member selection运算符”的指针,其作用是作为this指针的空间保留者。这也就是为什么static成员函数(没有this指针)的类型是“函数指针”,而不是“指向成员函数之指针”的原因。

使用一个“成员函数指针”,如果并不用于virtual function、多重继承、virtual基类等情况的话,并不会比使用一个“非成员函数指针”的成本更高。上述三种情况对于“成员函数指针”的类型以及调用都太过复杂。事实上,对于那些没有virtual函数或virtual基类,或multiple基类的类而言,编译器可以为它们提供相同的效率。


支持“指向virtual成员函数”之指针

我们知道,对一个非静态数据成员取其地址,将获得该函数在内存中的地址,然而面对一个虚拟函数,其地址在编译时候是未知的,所能知道的仅是virtual function在其相关的virtual table中的索引值。也就是说,对一个virtual member function取其地址,所能获得的只是一个索引值。


内联函数

一般而言,处理一个inline函数,有两个阶段:

一:分析函数定义。如果函数因其复杂度,或因其建构问题,被判断不可成为inline,它会被转为一个static函数。

二:真正的inline函数扩展操作是在调用的那一个点上。这会带来参数的求值操作(evaluation)以及临时性对象的管理。

在inline函数的扩展进行处理时,大部分的编译器厂商似乎认为不值得在inline支持技术上做详细的讨论。我们只有进入到汇编代码中,才可以看到是否真正的实现了Inline。
一般而言,inline函数中的每一个局部变量都必须被放在函数调用的一个封闭区段内,拥有一个独一无二的名字。Inline函数中的局部变量(展开处同名),再加上有副作用的参数,可能会导致大量临时性对象的产生。特别是如果它以单一表达式被扩展多次的话。如果一个Inline函数被调用多次的话,会产生大量的扩张码,使程序的大小暴涨。所以,我们需要小心的处理。



参考:

《深度探索C++对象模型》

http://blog.csdn.net/chinajane163/article/details/49975633

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值