Programming 版 (精华区)

发信人: JJason (C++ Primer), 信区: Programming
标  题: C++ FAQ(09):内联函数
发信站: 哈工大紫丁香 (2002年11月23日22:30:02 星期六), 站内信件

[9] 内联函数

(Part of C++ FAQ Lite, Copyright ? 1991-2001, Marshall Cline, 
cline@parashift.com)

简体中文版翻译:申旻,nicrosoft@sunistudio.com(东日制作室,东日文档)

-------------------------------------------------------

FAQs in section [9]:
[9.1] 内联函数是什么? 
[9.2] 内联函数是如何在安全和速度上取得折衷? 
[9.3] 为什么我应该用内联函数?而不是原来清晰的 #define 宏?  
[9.4] 如何告诉编译器使非成员函数成为内联函数? 
[9.5] 如何告诉编译器使一个成员函数成为内联函数? 
[9.6] 有其它方法告诉编译器使成员函数成为内联吗? 
[9.7] 内联函数保证执行性能更好吗?  

-------------------------------------------------------

[9.1] 内联函数是什么?
内联函数是代码被插入到调用者代码处的函数。如同 #define 宏,内联函数通过避免被调
用的开销来提高执行效率,尤其是它能够通过调用(“过程化集成”)被编译器优化。 

-------------------------------------------------------

[9.2] 内联函数是如何在安全和速度上取得折衷?
在 C 中,你可以通过在结构中设置一个 void* 来得到“封装的结构”,在这种情况下,
指向实际数据的 void* 指针对于结构的用户来说是未知的。因此结构的用户不知道如何解
释void*指针所指内容,但是存取函数可以将 void* 转换成适当的隐含类型。这样给出了
封装的一种形式。

不幸的是这样做丧失了类型安全,并且也将繁琐的对结构中的每个域的访问强加于函数调
用。(如果你允许直接存取结构的域,那么对任何能直接存取的人来说,了解如何解释 
void* 指针所指内容就是必要的了;这样将使改变底层数据结构变的困难)。

虽然函数调用开销是很小的,但它会被累积。C++类允许函数调用以内联展开。这样让你在
得到封装的安全性时,同时得到直接存取的速度。此外,内联函数的参数类型由编译器检
查,这是对 C 的 #define 宏的一个改进。 

-------------------------------------------------------

[9.3] 为什么我应该用内联函数?而不是原来清晰的 #define 宏? 
[Recently rewrote the sentence on #define being evil (on 7/00). Click here to 
go to the next FAQ in the "chain" of recent changes.] 
因为#define宏是在四处是有害的:罪状#1, 罪状#2, 罪状#3, 和 罪状#4。 

和 #define 宏不同的是,内联函数总是对参数只精确地进行一次求值,从而避免了那声名
狼藉的宏错误。换句话说,调用内联函数和调用正规函数是等价的,差别仅仅是更快:


 // 返回 i 的绝对值的宏
 #define unsafe(i)  \
         ( (i) >= 0 ? (i) : -(i) )
 
 // 返回 i 的绝对值的内联函数
 inline
 int safe(int i)
 {
   return i >= 0 ? i : -i;
 }
 
 int f();
 
 void userCode(int x)
 {
   int ans;
 
   ans = unsafe(x++);   // 错误!x 被增加两次
   ans = unsafe(f());   // 危险!f()被调用两次
 
   ans = safe(x++);     // 正确! x 被增加一次
   ans = safe(f());     // 正确! f() 被调用一次
 } 
和宏不同的,还有内联函数的参数类型被检查,并且被正确地进行必要的转换。

宏是有害的;非万不得已不要用。 

-------------------------------------------------------

[9.4] 如何告诉编译器使非成员函数成为内联函数?
声明内联函数看上去和普通函数非常相似: 


 void f(int i, char c); 
当你定义一个内联函数时,在函数定义前加上 inline 关键字,并且将定义放入头文件: 



 inline
 void f(int i, char c)
 {
   // ...
 } 
注意:将函数的定义({...}之间的部分)放在头文件中是强制的,除非该函数仅仅被单个
 .cpp 文件使用。尤其是,如果你将内联函数的定义放在 .cpp 文件中并且在其他 .cpp文
件中调用它,连接器将给出 “unresolved external” 错误。 

-------------------------------------------------------

[9.5] 如何告诉编译器使一个成员函数成为内联函数?
声明内联成员函数看上去和普通函数非常类似: 


 class Fred {
 public:
   void f(int i, char c);
 }; 
但是当你定义内联成员函数时,在成员函数定义前加上 inline 关键字,并且将定义放入
头文件中: 


 inline
 void Fred::f(int i, char c)
 {
   // ...
 } 
通常将函数的定义({...}之间的部分)放在头文件中是强制的。如果你将内联函数的定义
放在 .cpp 文件中并且在其他 .cpp 文件中调用它,连接器将给出“unresolved 
external”错误。 

-------------------------------------------------------

[9.6] 有其它方法告诉编译器使成员函数成为内联吗?
有:在类体内定义成员函数: 


 class Fred {
 public:
   void f(int i, char c)
     {
       // ...
     }
 }; 
尽管这对于写类的人来说很容易,但由于它将类是“什么”(what)和类“如何”(how)工作
混在一起,给阅读的人带来了困难。我们通常更愿意在类体外使用 inline 关键字定义成
员函数来避免这种混合。这种感觉所基于的认识是:在一个面向重用的世界中,使用你的
类的人有很多,而建造它的人只有一个(你自己);因此你做任何事都应该照顾多数而不
是少数。 

-------------------------------------------------------

[9.7] 内联函数保证执行性能更好吗? 
[Recently explained "code bloat" and also added lots of if's, and's and but's 
(on 4/01). Click here to go to the next FAQ in the "chain" of recent 
changes.] 
不。

小心过度使用内联函数可能导致代码膨胀。在页面调度环境中,它可能会给执行性能带来
负面影响。

代码膨胀术语只表示代码的尺寸会增大(膨胀)。在有关内联函数的上下文中,更关心的
是内联函数会增加执行代码的尺寸,并导致操作系统不稳定。这意味着操作系统要花费大
部分的时间从磁盘取出代码。

当然,内联函数也可能减小执行代码的尺寸。看上去反了,其实是真的。特别是,调用函
数的代码总量有时会大于展开的内联函数的代码总量。这样的情况会发生于非常小的函数
,当优化器能删除很多冗余代码时——也就是当优化器能使长的函数变短时,也可能会发
生于长的函数。

因此结论就是:没有简单的定论。你必须因地制宜。不要使得答案象这样的单纯化,“不
要用内联函数”或“总是使用内联函数”或“当且仅当函数代码少于 N 行时用内联函数”
。这种一刀切的方法可能用起来非常简单,但是它们产生的并不是最佳结果。 

-------------------------------------------------------

 E-mail the author
[ C++ FAQ Lite | Table of contents | Subject index | About the author | ? | 
Download your own copy ]
Revised Apr 8, 2001 
 
--

     人生,就是一团欲望:
     欲望没有满足的时候就是痛苦,
     欲望被满足的时候就是无聊;
     人生就是在痛苦与无聊之间徘徊。

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.229.69]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:6.988毫秒