深度探索c++对象模型第四章笔记下

4.4 指向Member Function的指针

在之前的章节中,已经知道取一个nonstatic data memeber的地址,得到的结果就是该member在class布局中的bytes位置(再加上1),我们知道的是,它是一个不完整的值,它需要被绑定于某个class object的地址上,才能被存取
取一个nonstatic member function的地址,如果该函数是nonvirtual ,则得到的结果是它在内存中真正的地址,但是这个值也是不完全的,它也需要被绑定于某个class object的地址上,才能通过它调用该函数。所有的nonstatic member functions都需要对象的地址

支持“指向Virtual Member Functions”之指针

float (Point::*pmf)() =&Point::z;
Point *ptr=new Point3d();

pmf,一个指向member function的指针,被设值为Point::z()(一个virtual function)的地址。
ptr则被指定以一个Point3d对象,如果我们直接经由ptr调用z():ptr->z();
则被调用的是Point3d:?(),但如果我们从pmf间接调用z():(ptr->*pmf)();
在虚拟机制的作用下,Point3d:z()被调用。那么它是如何实现的呢?
在之前讲过,对一个nonstatic member function取其地址,将获得该函数在内存中的地址。然而如果面对一个virtual function,其地址在编译时期也是未知的,所能知道的仅是virtual function与其相关的virtual table中的索引值。也就是说,对一个virtual memeber function取其地址,所能获得的只是一个索引值

在多重继承下,指向Member Functions的指针

为了让指向member functions的指针也能支持多重继承和虚拟继承,

“指向Member Functions 之指针”的效率

当函数cross_product()函数经由以下方式调用:

  • 1、一个指向nonmember function的指针;
  • 2、 一个指向class member function的指针;
  • 3、一个指向virtual member function的指针;
  • 4 、多重继承下的nonvirtual 及virtual member function call;
  • 5、虚拟继承下的nonvirtual 及virtual member function call。

4.5 Inline Functions

关键字inline只是一项请求,如果这项请求被接受,编译器就必须认为他可以用一个表达式合理的将整个函数扩展开。这里的“编译器相信它可以合理地扩展一个inline 函数”时,就是说在某个层次上,其执行成本比一般的函数调用及返回机制所带来的负荷低
一般而言,处理一个inline函数,有两个阶段:

  • 1、分析函数定义,已决定函数的“intrinsic inline ability”.如果函数因其复杂度,或因其建构问题,被判断不可称为inline,它会被转换为一个static函数,并在“编译器模块”内产生对应的函数定义。理想情况下,链接器会将被产生出来的重复东西清理掉。
  • 2、真正的inline函数扩展操作是在调用的那一点上。这会带来参数的求值操作以及临时性对象的管理。同样是在扩展点,编译器将决定这个调用是否“不可为inline”。

形式参数(Formal Arguments)

在inline扩展期间,每一个形式参数都会被对应的实际参数取代。如果说有什么副作用,就是可能会引入临时性对象。也就是说,如果实际参数是一个常量表达式,我们可以在替换之前先完成其求值操作:后继的inline替换,就可以把常量直接“绑”上去。如果既不是常量表达式,也不是有副作用的表达式,那就就可以直接替换。

inline int
min(int i,intj)
{
return i<j?i:j;
}

inline int 
bar()
{
int minval;
int val1=1024;
int val2=2048;

/*(1)*/minval =min(val1,val2);
/*(2)*/minval=min(1024,2048);
/*(3)*/minval=min(foo(),bar()+1);
return minval;
}

那么标记为(1)的那一行会被扩展为:

//(1)参数直接替换为
minval=val1<val2?val1:val2;

那么标记为(2)的那一行会直接替换为常量:

//(2)代换之后,直接使用常量
minval=1024;

那么标记为(3)的那一行则会引发参数的副作用,它需要导入一个临时对象,以避免重复求值

//(3)有副作用,所以导入临时对象
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;
}

这个局部变量需要什么额外的支持或处理呢?
inline被扩展开之后,为了维护其局部变量,可能会成为下面这个样子:

{
	int local_var;
	int minval;

	//将inline 函数的局部变量处以“mangling”操作
	int __min_lv_minval;
	minval=
	(__min_lv_minval=
		val1<val2?val1:val2, __min_lv_minval);		
}

一般而言,inline函数中的每一个局部变量都必须被放在函数调用的一个封闭区段中,拥有独一无二的名称。如果inline函数以单一表达式扩展多次,那么每次扩展都需要自己的一组局部变量。如果inline函数以分离的多个式子被扩展多次,那么只需要一组局部变量,就可以重复使用
inline函数中的局部变量,再加上有副作用的参数,可能会导致大量临时性的对象的产生

Inline函数对于封装提供了一种必要的支持,可以有效存取封装与class中的nonpublic数据。它同时也是c程序中大量使用的#define的一个安全替代品。然而一个Inline函数如果被调用太多次,会产生大量的扩展码。
参数带有副作用,或是以一个单一表达式做多重调用,或是在inline函数中有多个局部变量,会产生临时性对象,编译器也许能(也许不能)够把它们移除。
inline中如果再有inline,可能会使一个表面上看起来平凡的inline却因其连锁复杂度而没办法扩展开来。

与non-inline函数比起来,inline需要更加小心的处理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值