`
kmplayer
  • 浏览: 512690 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

条款33:明智地运用inline

阅读更多
1,inline函数:看起来像函数,动作起来像函数,比宏好得多,不需要蒙受函数调用带来的额外负担.
而且,编译器最佳化机制通常用来浓缩那些不含函数调用动作的代码,所以当你inline一个函数时,编译器就有能力在函数体身上执行某种最佳化.

2,inline函数背后的观念:将对函数的每一个调用行为都用函数代码取代之.
显然这么做,会增加你目标代码的大小.
inline行为造成的程序代码膨胀可能会导致病态的换页,或是降低cache的命中率.

3,特别注意的是:
inline指令就像register指令一样,只是对编译器的一种提示,而不是强制命令.
大部分编译器会拒绝将复杂的函数(内含循环或递归)inline化,所有的虚拟函数都会阻止inline的进行.
这是因为virtual意味着"等待,直到运行时期再确定调用哪一个函数."
inline意味着"在编译阶段,将调用动作以被调用函数的本体取代之"

4,理论和实际在这个问题上很可能分道扬镳:
inline函数的定义几乎总是被放在头文件中.
举个例子:
// This is file example.h
inline void f() { ... }          // definition of f

...

// This is file source1.cpp
#include "example.h"             // includes definition of f
...                              // contains calls to f

// This is file source2.cpp
#include "example.h"             // also includes definition of f
...                              // also calls f
假设f没有被inline,那么source1和source2目标文件中均含有f的函数,均未强化的,连接这两个模块时,就会造成连接错误.
因此,旧规则宣布"编译器对待一个inline失败的函数,犹如函数被声明为static似的".
由此可见,在旧的规则下,如果一个inline函数inline失败,你不仅必须在每次调用函数时付出函数调用的成本,还得承受代码体积增加的事实,因为每个调用f的编译单元都有它们自己的一份f函数吗,以及f中的每一个static变量.

5,有的情况,编译器必须为已经成功inline的函数产生一个函数体.
例如:你的程序曾经取一个inline函数的地址,编译器就必须为此函数产生一份函数实体.
如:
inline void f() {...}            // as above

void (*pf)() = f;                // pf points to f

int main()
{
  f();                           // an inline call to f

  pf();                          // a non-inline call to f
                                 // through pf
  ...
}
注:新规则之下,不论牵扯的编译单元有几个,只有一个out-of-line的f副本被产生出来.

很多时候,编译器会为constructor和destructor等函数产生out-of-line副本,从而使自己可以获得这些函数的指针.
例如:
class Base {
public:
  ...private:
  string bm1, bm2; // base members 1 and 2
};class Derived: public Base {
public:
  Derived() {}                  // Derived's ctor is
  ...                           // empty -- or is it?

private:
  string dm1, dm2, dm3;         // derived members 1-3
};
因为C++有很多保证一定会发生的事情,如"产生一个对象时,其base对象和每个data members都会被自动构造".
这些事情一定不是凭空发生的,"如何发生"是编译器实现者的权限.编译器可能会将一些代码安插到某些地方.
事实上,constructor和destructor往往不是inline的最佳候选人.
例如:
// possible implementation of Derived constructor
Derived::Derived()
{
  // allocate heap memory for this object if it's supposed
  // to be on the heap; see Item 8 for info on operator new
  if (this object is on the heap)
    this = ::operator new(sizeof(Derived));

  Base::Base();                  // initialize Base part

  dm1.string();          // construct dm1
  dm2.string();          // construct dm2
  dm3.string();          // construct dm3
}
这样一来Derived()的大小就增加了,注意string的构造函数也要增加,这样Derived将变得很大,是否将Derived()inline化,答案显而易见.

6,inline带来的另一个冲击:
inline函数无法随着程序的升级而升级.
因为一旦inline函数f被改变,所有用到f的程序都必须重新编译.
如果f不是inline,f被修改后,用户只需重新连接即可,远比重新编译的负担小.

7,inline函数中的static对象常会展现反直观的行为.
因此如果函数带有static对象,通常要避免将它声明为inline.

8,最重要的原因:大部分除错器对inline函数束手无策.

9,总结:该不该使用inline的策略
一开始,不要将任何函数声明为inline,或至少将inline范围限制在那些实在非常琐屑平凡的函数身上.
例如下面的age函数:
class Person {
public:
  int age() const { return personAge; }
  ...
private:
  int personAge;
  ...
};

根据80-20的经验法则,找出程序中占重要效率低位的函数,然后向办法将它们inline化.
分享到:
评论

相关推荐

    Effective.C++.中文第二版.50条款doc文档.chm

    条款33: 明智地使用内联 条款34: 将文件间的编译依赖性降至最低 第六章 继承和面向对象设计 条款35: 使公有继承体现 "是一个" 的含义 条款36: 区分接口继承和实现继承 条款37: 决不要重新定义继承而来的非虚函数 ...

    Effective C++(第三版)

    条款33:避免遮掩继承而来的名称 avoid hiding inherited names. 条款34:区分接口继承和实现继承 differentiate between inheritance of interface and inheritance of implementation. 条款35:考虑virtual函数...

    Effective C++ 中文版

    条款33:避免遮掩继承而来的名称 条款34:区分接口继承和实现继承 条款35:考虚virtual函数以外的其他选择 条款36:绝不重新定义继承而来的non-virtual函数 条款37:绝不重新定义继承而来的缺省参数值 条款38:...

    display:inline、block、inline-block的区别(转的)

    本篇文章将深入探讨`display:inline`、`display:block`以及`display:inline-block`这三种主要的显示模式之间的区别,帮助你更好地理解它们在实际开发中的应用。 首先,我们来看`display:inline`。这种模式使得元素...

    详解CSS中的display:flex||inline-flex属性

    主要给大家介绍了CSS中的display:flex和display:inline-flex属性,文中分别通过两段实例代码给大家介绍了display:flex和display:inline-flex的使用效果,感兴趣的朋友们可以参考借鉴,下面来一起看看吧。

    display:inline-block的实际应用

    在提供的压缩包文件 "display-block应用" 中,可能包含了使用 `display:inline-block` 进行布局的示例代码或教程,通过学习和实践这些案例,可以帮助你更好地理解和掌握这一技巧。 总之,`display:inline-block` 是...

    每天学点C++(C++实例教程:教程+源码)inline函数.zip

    C++是一种强大的、通用的编程语言,以其面向对象特性、高效性能和丰富的库支持而闻名。...因此,明智地选择何时使用`inline`是每个C++程序员应具备的技能。这个教程和源码将帮助你在这个过程中迈出坚实的一步。

    高效C++:从C到C++

    **条款33:明智地使用内联** - **背景**:`inline`关键字用于指示编译器尝试内联函数调用。 - **原因**:过度使用`inline`可能会导致代码膨胀。 - **示例**: ```cpp inline int add(int a, int b) { return a +...

    使用font-size:0 来去掉inline-block元素之间的空隙方法

    现代浏览器的最新版都支持inline-block,只有该死的ie6、7不支持inline-block,但ie6、7可以通过 display:inline;zoom:1;来模拟。 下面是inline-block兼容的代码: view sourceprint?display:inline-block;*...

    display inline怎么用

    同时,我们也了解了其他几种常见的`display`值及其用途,这对于更好地理解和运用CSS布局具有重要意义。 总之,在处理复杂的页面布局时,正确使用`display`属性可以极大地提高工作效率,帮助开发者快速构建出既美观...

    inline hook库

    综上所述,inline hook是一种强大的编程技术,能够灵活地改变程序的行为。然而,它也带来了一系列挑战,包括兼容性、安全性和调试难度等。在使用inline hook时,开发者需要充分了解其原理和潜在风险,才能有效且安全...

    有关display:inline-block在FF出现空白的解决方案

    在网页布局设计中,`display:inline-block` 是一个非常常用且强大的CSS属性,它允许元素在保持块级元素特性的同时,像内联元素一样并排显示。然而,在不同浏览器之间,尤其是在Firefox(FF)上,可能会遇到一些显示...

    内核三步走实现Inline Hook

    内核三步走实现Inline Hook是一种在操作系统内核层面对函数进行动态拦截和修改的技术,常用于系统调试、性能分析以及安全监控等场景。本文将详细介绍如何通过三个关键步骤实现内核级的Inline Hook。 首先,理解...

    inline函数说明(转载)

    本文将深入探讨`inline`函数的工作原理、使用场景及其潜在的陷阱,帮助开发者更好地理解和应用这一特性。 #### inline函数的基本概念 `inline`函数的目的是为了减少程序运行时的函数调用开销。在没有`inline`的...

    关于css display: inline block inline-block的区别分析

    `display: inline`、`display: block` 和 `display: inline-block` 是三种常见的值,它们各自具有不同的行为和用途。 1. **display: inline** - `display: inline` 使得元素以行内元素的方式呈现,这意味着它们会...

    inline-java:实现从Haskell调用任何JVM函数

    inline-java: 实现从Haskell调用任何JVM函数

    Inline Hook_inlinehook_x86_x64_64位HOOK_

    Inline Hook是一种技术,主要用于在程序运行时修改函数的行为。它涉及到计算机编程中的底层技术,特别是逆向工程和软件调试领域。Inline Hook的核心是通过在原函数的代码中插入额外的指令来实现对函数调用的拦截,...

    内联函数inline使用实例

    内联函数(inline)在C++编程语言中是一种优化手段,用于提高程序的运行效率。它的主要作用是在编译期间将函数体插入到每个调用该函数的地方,从而避免了函数调用时的开销,如函数调用的压栈、跳转以及返回等过程。...

Global site tag (gtag.js) - Google Analytics