《深入探索C++对象模型》第四章:Function语意学

26 篇文章 1 订阅

1、Member的各种调用方式

在C++中,支持三种类型的member functions:static、nonstatic和virtual,每一种类型被调用的方式都不一样,下面我们分别来探讨一下:

(1)Nonstatic

C++要求非静态成员函数必须要和一般的非成员函数具有相同的效率。采取的措施是通过以下步骤将成员函数实例转换为对等的非成员函数实例:

a.改写函数的原型(signature)以安插一个额外的参数this到成员函数中,用以提供一个存取管道

b.将每一个对非静态成员变量的存取操作改为经由this来存取

c.将成员函数重新写成一个外部函数,将函数名称经过“mangling”处理,使其在程序中成为独一无二的词汇,这个矫正操作不包含函数的返回值

关于mangling操作,即name mangling操作有一个成熟的规则:

a.成员函数的名称前面加上class的名称

b.为了应付重载,再在后面加上参数链表,记住,返回值并不作为参考

(2)virtual

我们假设foo()是一个虚函数,那么以下的调用:

ptr->foo()
将被内部转化为:

(*ptr->vptr[1])(ptr)
其中vptr表示由编译器产生的指针,指向virtual table,注意一个class内可能含有多个vptrs。但是对于以下调用:

obj.foo()
如果编译器将其转换为上面一样的调用,虽然语意正确,但是没有必要。因为由对象直接调用不会产生多态,在编译期间即可决议,即这种操作应该总是被编译器像对待一般非静态成员函数一样加以决议——重写函数原型,转换为同等的非成员函数调用。这就是为什么即使覆盖了虚函数表,用对象调用虚函数也不会产生多态的原因。

(3)static

其实只要注意静态成员函数没有this指针,其它的转换操作和非静态是一样的,以下特性都是根据此特性来的:

a.不能直接存取其class的非静态成员

b.不能被声明为const、volatile、virtual,因为这些都需要this指针来支持

c.不需要经由class object才被调用

2、Virtual Member Function
在C++中,多态表示以一个public base class的指针或引用,寻址出一个derived class object的意思,比如下面的声明:

Ponit *ptr;
ptr = new Point2d;
这种多态形式被称为是消极的,可以在编译期间完成(除了虚基类,不是很明白为什么),当被指出的对象被真正使用时,多态变成积极的,关于虚函数的运作机制,有很多文章可以参考,这里不做赘述。下面我们来看看多重继承下的虚函数

(1)多重继承下的虚函数
这里的多重继承是非虚继承,就是一般情况下的继承,比如下列继承体系:

class base1{ ... };
class base2{ ... };
class derived:public base1,public base2{ ... };
派生类支持虚函数的困难度,统统落在了base2的身上。base1的多态是毫无困难的,因为base1的内容在派生类的上方,可以使得完美表现多态,而base2就悲剧了,在中间,想要支持多态,有点困难,比如下面的例子:

base2 *pbase2 = new derived;
//编译器会产生以下代码
derived *temp = new derived;
base2 *pbase2 = temp?temp+sizeof(base1):0
因为编译器要确保派生类能够表现的和基类一样,“is a”关系。当程序员需要删除pbase2指针的时候,指针必须再一次调整,以之处完整对象的起始点。
(2)虚拟继承下的虚函数

因为虚拟继承需要保证相同的基类只出现一次,并且派生类要表现的和基类一样,“is a”关系,所以需要对虚函数表进行调整,增加偏移量,具体细节请看下面的这个博文。

C++多继承和虚拟继承的内存布局

 说的十分详细
3、内联函数

在C++中,我们并不能强迫将任何函数都变成lnline,虽然有inline这个关键字,但这只是个请求,编译器并不一定会接受。一般而言,处理一个内联函数有两个阶段:

a.分析函数定义,以决定函数的“intrinsic inline ability”,即判断函数的复杂程度,申明为内联是否合算

b.真正的内联函数扩展操作是在调用的那一点上,这会带来参数的求值操作和临时性对象的管理。

(1)形式参数的处理

在inline扩展期间,每一个形式参数读会被对应的实际参数所代替,比如下面这个例子:

inline int
min(int i, int j)
{
    return i < j?i:j;
}
//下面是三个调用操作
int minval;
int val1 = 1024;
int val2 = 2048;
/*1*/minval = min(val1,val2);
/*2*/minval = min(1024,2048);
/*3*/minval = min(foo(),bar());

对于第一个调用,会转换为minval = val1 < val22?val1:val2。第二个调用直接给出结构minval=1024。第三个调用会引发重复求值,故引入一个临时变量:

int t1,t2;
minval = (t1 = foo()),(t2 = bar()),t1<t2?t1:t2;
如果不引入临时变量,则会转换成这样:

minval = foo() < bar()? foo():bar();
(2)局部变量的处理

如果inline中含有局部变量该如何处理?

inline int
min(int i, int j)
{
    int minval = i < j? i:j;
    return minval;
}
为了维护这个局部变量,可能需要对这个局部变量进行名字矫正操作,使其变得独一无二。

OK that's all;


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值