• 条款30 :透彻了解inline的里里外外


    为什么用Inline函数?
    看起来像函数,但是没有函数调用时的压栈出栈的开销。并且,我们知道编译器最优化的机制通常被设计用来浓缩那些“不含函数调用”的代码。所以当你inline了某个函数,编译器就有能力对它执行语境相关最优化。

    但是并不是所有的函数都变成inline都好。我们知道,inline后,程序的目标代码是会变大的。 如果在一个内存较小的机子上运行,会导致过多的换页行为,降低高速缓存着墨的命中率,以及伴随而来的低效率。
    换个角度来说,如果inline的本体很小,编译器针对“函数本体”所产出的码可能比调用所产出的码小,那确实很好,会减少目标码的量,提高高速缓存装置的命中率。

    Inline函数通常一定被置于头文件内,因为大多数的建置环境在编译过程中进行inline,而为了将一个“函数调用”替换为“被调用函数的本体”,编译器必须知道那个函数长什么样子。
    现在我们要理解“inline是个申请,编译器可以加以忽略”。
    大部分编译器会忽略太过复杂的函数inline,而对所有的virtual函数的调用也都会使inline落空。想想这是为什么?因为virtual表示“等待,直到运行时”,而inline意味着执行前,先将调用动作替换为被调用函数的本体“,如果编译器不知道该调用哪个函数,你就很难责备它们拒绝将函数本体inline。

    有时,我们虽然有意愿将某个函数inline,但是不一定成功,这还取决于你的调用方式:比如你想通过函数指针来调用。

    inline void f() {...} // inline函数
    void (*pf) () = f; // 定义一个指针指向函数f
    ...
    f(); //会执行inline
    pf(); //不会被inline,因为它通过函数指针来调用

    想一想为什么通过函数指针不可以呢?我们知道在编译时候,我们只知道pf是一个函数指针,但是我们并不知道里面的值。直到运行时,我们才知道。是不是。所以它不能通过函数指针inline.
    实际上构造函数和析构函数往往是inline的糟糕后选人。

    class Base{
    public:
        ...
    private:
        std::string bm1 ,bm2;
    };



    class Derived:public Base{
    public:
        Derirved(){} //真得为空吗?
        ...
    private:
        std::string dm1,dm2,dm3;
    };



    我们知道,在对象被创建时和被销毁时,有什么多的事情要做。比如调用基类的构造函数…,所以说在派生类中的Derived()函数并不是为空的。编译器会增加很多的东西。比如:

    Derirved(){
            Base::Base();
            try
            {
                dm1.std::string::string();
            }
            catch (...)
            {
                Base::~Base();throw;
            }
            try
            {
                dm2.std::string::string();
            }
            catch (...)
            {
                dm1.std::string::~string();
                Base::~Base();throw;
            }
            try
            {
                dm3.std::string::string();
            }
            catch (...)
            {
                dm2.std::string::~string();
                dm1.std::string::~string();
                Base::~Base();throw;
            }
        }



    这已经准备反映了构造函数并非为空。这说明不应该全部把构造函数定义在类定义体中(因为定义在这里面,就是隐式的inline)。同样的道理也适合于析构函数。
    所以说,你会明白把一个函数设置为inline是有很大的学问的。

    这里面还有一个问题是:如果inline函数内部做了改动,那么所有调用此内联函数的代码都会要重新编译。而如果一个非inline函数内部做了改动,那么只需要重新进行链接就好了。

    请记住:

    • 将大多数inline限制在小型,被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。
  • 相关阅读:
    第8章 字符串
    第7章 方法
    第6章 类和对象
    第5章 数组
    第4章 循环结构、break与continue
    第3章 选择结构
    第2章 变量、数据类型和运算符
    Codeforces Round #426 (Div. 2)
    CCNA学前基础一
    Tinkoff Challenge
  • 原文地址:https://www.cnblogs.com/loveyakamoz/p/2772420.html
Copyright © 2020-2023  润新知