4.5 Inline Functions

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

  • 分析函数定义,以决定函数的“intrinsic inline ability”(本质的inline能力)。

如果函数因其复杂度,或因其构建问题,被判断不可成为inline,它会被转为一个static函数,并在“被编译模块”内产生对函数的定义。

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

同样是在扩展点上,编译器将决定这一调用是否“不可为inline”。inline函数如果只是一个表达式,则其第二或后继调用操作就不会被展开:


new_pt.x(lhs.x() + rhs.x());

//不会被展开成这样
new_pt.x = lhs._x + x_5pointFV(&rhs);

//大概是这样
new_pt.x(lhs._x + rhs._x);

形式参数(Formal Arguments)

在inline扩展期间,每一个形式参数都会被对应的实际参数取代。一般而言,面对“会带来副作用的实际参数”(比如多次求值),通常都需要引入临时对象。如果实际参数是一个常量表达式,我们可以在替换之前先完成求值操作,然后替换;如果不是不是个常量表达式,也不是个带副作用的表达式,那么就直接代换。

如下的inline函数:

inline int min(int i,int j)
{
	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)
//参数直接替换
minval = val1 < val2 ? val1 : val2;

//(2)直接拥抱常量
minval = 1024;

//(3)那一行引发副作用,需要导入一个临时对象
//以避免重复求值
int t1;
int t2;

minval = (t1 = foo(),t2 = bar() + 1),
	t1 < t2 ? t1 : t2;

局部变量 

在inline函数中加入一个局部变量,如下:

inline int
min(int i,int j)
{
	int minval = i < j ? i : k;
	return minval;
}

//下面是调用
int local_var;
int minval;

//...
minval = min(val1,val2);

inline被扩展的样子:

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

一般而言,inline函数中每一个局部变量都必须放在函数调用的一个封闭区间,有用独一无二的名字。如果inline函数以单一表达式扩展多次,则每次扩展都需要自己的一组局部变量。如果inline函数以分离的多个式子被扩展多次,那么只需要一组局部变量,就可以重复使用。

inline函数中的局部变量,再加上有副作用的参数,可能会导致大量临时性对象的产生。特别是如果它以单一表达式被扩展多次的话:

minval = min(val,val2) + min(foo(),foo() + 1);


//扩展成如下
//为局部变量产生临时变量
int _min_lv_minval_00;
int _min_lv_minval_01;

//为防止副作用值而产生临时变量
int t1;
int t2;

minval = 
	((_min_lv_minval_00 = 
	 val1 < val2 ? val1 : val2),
	 _min_lv_minval_00)
	 +
	 ((_min_lv_minval_01 = (t1 = foo()),
	 (t2 = foo() + 1),
	 t1 < t2 ? t1 : t2),
	 _min_lv_minval_01);

inline函数对于封装提供了一种必要的支持,可以有效的存取封装于class中的nonpublic 数据,同时它也是C程序大量使用的#define的一个安全替代品。然而inline函数如果被调用太多次的话,会产生大量的扩展码,使程序膨胀。

参数带有副作用的,或是以一个单一表达式做多重调用,或是在inline函数中有多个局部变量,都会产生临时对象,编译器也许(或也许不)能够它们移除。此外,inline中再由inline,可能会使一个表面看起来平凡的inline却因其连锁复杂度而没办法扩展开来。这种情况发生于赋值class体系下的constructor,或是object体系中一些表面上并不正确的inline调用所组成的串链。

既要安全又要效率的程序,inline函数提供了强大而有力的工具,然而与non-inline函数比起来,它们需要更加小心地处理。

猜你喜欢

转载自blog.csdn.net/weixin_28712713/article/details/84091034
4.5