文档详情

《《C++箴言:理解inline的介入和排除》.pdf

发布:2015-09-28约6.88千字共5页下载文档
文本预览下载声明
C++箴言:理解inline 的介入和排除 inline 函数——多么棒的主意啊!它们看起来像函数,它们产生的效果也像函数,它们在各 方面都比宏好得太多太多,而你却可以在调用它们时不招致函数调用的成本。你还有什么更 多的要求呢? 实际上你得到的可能比你想的更多,因为避免函数调用的成本只是故事的一部分。在典 型情况下,编译器的优化是为了一段连续的没有函数调用的代码设计的,所以当你 inline 化 一个函数,你可能就使得编译器能够对函数体实行上下文相关的特殊优化。大多数编译器都 不会对 outlined 函数调用实行这样的优化。 然而,在编程中,就像在生活中,没有免费午餐,而 inline 函数也不例外。一个 inline 函数背后的思想是用函数本体代替每一处对这个函数的调用,而且不必拿着统计表中的 Ph.D. 就可以看出这样可能会增加你的目标代码的大小。在有限内存的机器上,过分热衷于 inline 化会使得程序对于可用空间来说过于庞大。即使使用了虚拟内存,inline 引起的代码 膨胀也会导致附加的分页调度,减少指令缓存命中率,以及随之而来的性能损失。 在另一方面,如果一个 inline 函数本体很短,为函数本体生成的代码可能比为一个函 数调用生成的代码还要小。如果是这种情况,inline 化这个函数可以实际上导致更小的目标 代码和更高的指令缓存命中率! 记住,inline 是向编译器发出的一个请求,而不是一个命 令。这个请求能够以显式的或隐式的方式提出。隐式的方法就是在一个类定义的内部定义一 个函数: classPerson{ public: ... int age() const { return theAge; } // animplicit inline request: ageis ... // definedin a classdefinition private: int theAge; }; 这样的函数通常是成员函数,不过我们知道友元函数也能被定义在类的内部,如果它们 在那里,它们也被隐式地声明为 inline。 显式的声明一个 inline 函数的方法是在它的声明之前加上 inline 关键字。例如,以 下就是标准 max 模板(来自 algorithm)经常用到的的实现方法: templatetypename T // anexplicit inline inline constT std::max(constT a, constT b)// request: std::max is { return a b ?b : a;} // precededbyinline max 是一个模板的事实引出一个观察结论:inline 函数和模板一般都是定义在头文件中的。 这就使得一些程序员得出结论断定函数模板必须是 inline.这个结论是非法的而且有潜在 的危害,所以它值得我们考察一下。 inline 函数一般必须在头文件内,因为大多数构建环 境在编译期间进行 inline 化。为了用被调用函数的函数本体替换一个函数调用,编译器必 须知道函数看起来像什么样子。(有一些构建环境可以在连接期间进行 inline 化,还有少 数几个——比如,基于 .NET Common Language Infrastructure (CLI) 的控制环境—— 居然能在运行时 inline 化。然而,这些环境都是例外,并非规则。inline 化在大多数 C++ 程序中是一个编译时行为。) 模板一般在头文件内,因为编译器需要知道一个模板看起来像什么以便用到它时对它进 行实例化。(同样,也不是全部如此。一些构建环境可以在连接期间进行模板实例化。然而, 编译期实例化更为普遍。) 模板实例化与 inline 化无关。如果你写了一个模板,而且你认 为所有从这个模板实例化出来的函数都应该是 inline 的,那么就声明这个模板为 inline, 这就是上面的 std::max 的实现被做的事情。但是如果你为没有理由要 inline 化的函数 写了一个模板,就要避免声明这个模板为 inline(无论显式的还是隐式的)。inline 化是 有成本的,而且你不希望在毫无预见
显示全部
相似文档