2025年inline函数_深度剖析C++中的inline函数

inline函数_深度剖析C++中的inline函数点蓝色字关注 CurryCoder 微信公众号 CurryCoder 的程序人生 怕什么真理无穷 进一寸有一寸的欢喜 1 inline 函数的爱恨两难 内联函数比宏优点好很多 详细原因请参见 尽量以 const enum inline 替换 define

大家好,我是讯享网,很高兴认识大家。

24784123807900322f6ddd9c7e3c76b3.gif
讯享网点蓝色字关注“CurryCoder”

微信公众号:CurryCoder的程序人生 

怕什么真理无穷,进一寸有一寸的欢喜

1edb3b649fee123e43c8f7818364329d.png


1.inline函数的爱恨两难

内联函数比宏优点好很多,详细原因请参见 尽量以const、enum、inline替换#define   。 调用内联函数不需要承受函数调用所导致的额外内存开销, 编译器最优化机制通常被设计用来浓缩那些“不含函数调用”的代码,所以当一个函数为内联函数时,或许编译器就因此有内联对其执行语境相关的最优化。 “世界上没有免费的午餐”,内联函数背后的整体思想是: 对一个函数的调用都以函数本体替换它,但这同时会增加你的目标码大小。在一个内存有限的机器上,过度使用内联函数会造成程序体积太大。即使用有虚拟内存,内联函数造成的代码膨胀也会导致额外的换页行为,降低指令高速缓存器的命中率。 另一方面来说,如果内联函数的本体很小,编译器针对函数本体所产生的码可能比函数调用所产生的码更小。因此,将函数设置为inline,确实导致更小的目标码和较高的命中率。

2.inline函数卑微在线求助

inline只是对编译器的一个申请建议,不是强制命令,编译器可以选择对你的建议置之不理 。 这项申请可以隐式声明也可以显式声明,隐式声明是将函数定义在类的内部,如下所示:
class Person{ 
  
    
  
public:
    // ...
    int age() const { return mAge;}   // 隐式的内联函数
private:
    int mAge;
};

讯享网 显式声明的方法则是在函数定义式前面加上关键字inline,如下面函数模板:
讯享网template<typename T>
inline const T& max(const T& a, const T& b){
    return a }
上述显式的声明方法中 , 值得注意的一点是:inline函数和函数模板通常都被定义于头文件中。但是,函数模板未必一定是内联的!!!inline函数通常一定被放置在头文件中,因为大多数构建环境在编译过程中进行内联动作,为了将一个函数调用替换为被调用函数的本体,编译器必须知道那个函数是啥样。 内联动作在大多数C++程序中是编译期的行为。 函数模板通常也被放置在头文件中,因为它一旦被使用,编译器为了将它具体化,需要知道它的样子。某些构建环境中,可以在连接期执行模板具体化,只不过编译期完成具体化的动作比较常见。 3.inline函数与虚函数的爱恨纠缠

大部分编译器拒绝将太复杂(带有循环或递归、switch-case语句)的函数设置为inline,而且所有对虚函数的调用也都会使inline失效。这是因为虚函数是直到运行期才会确定调用哪个函数,而inline函数意味着执行前先把调用动作替换为被调用函数的本体,编译器工作在编译期。如果编译器都不知道该调用哪个函数,你也就明白了为啥虚函数不建议设置为inline。更多详情原因请看这里https://www.jianshu.com/p/84add

4.糟糕的内联选择:构造函数与析构函数

不要被你的眼睛所欺骗,下面子类构造函数真的是空的吗???

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

class Derived: public Base{
public:
    Derived(){}  // Derived构造函数真的是空的吗???
    // ...
private:
    string dm1, dm2, dm3;
};
当你使用new,动态创建的对象被其构造函数自动初始化;当你使用delete,对应的析构函数会被调用。当你创建一个对象时,基类及其每个成员变量都会被自动构造;当你销毁一个对象时,从子类开始执行析构动作。 如果有个异常在对象构造期间抛出,该对象已构造好的那一部分会被自动销毁 。你的程序内一定有某些代码让那些事情发生,这些代码即编译器在编译期间产生并安排在你代码中的某个地方。有时候,可能就存在于你的构造函数和析构函数中。上面表面上看起来空的Derived构造函数所产生的代码,如下所示:
讯享网Derived::Derived(){ 
  
    
  
    Base::Base();
    try{
        dm1.string();
    }
    catch(...){
        Base::~Base();
        throw;
    }

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

     try{
        dm3.string();
    }
    catch(...){
        dm2.string();
        dm1.string();
        Base::~Base();
        throw;
    }
}
上面的代码并不能代表编译器真正产生的代码,因为真正的编译器会以更复杂的做法来处理异常。尽管如此,上面的代码已经能反映Derived的空白构造函数必须提供的行为。不论编译器在其内部所做的异常处理有多么复杂,Derived构造函数至少一定会陆续调用成员变量和基类两者的构造函数,而那些调用会影响编译器是否对此空白函数执行内联动作。 最后,程序库的设计者必须知道:内联函数无法随着程序库的升级而升级。如果fun()是程序库中的一个内联函数,客户将fun()本体编进其程序中,一旦程序库设计者决定改变fun(),所有用到函数f()的客户端程序都必须重新编译。如果fun()是non-inline函数,一旦它有所修改客户端只需要重新连接即可。 对程序开发而言,大部分调试器对内联函数都束手无策 ,因为你无法在一个并不存在的函数内部设置断点进行调试呢?哪些函数可以设置为inline呢?我的建议是:首先不要将任何函数声明为inline,或者将inline实施范围局限在那些一定可以成为inline的场合。 5.总结 (1) 将大多数inline行为限制在小型、被频繁调用的函数身上。这可以使以后的调试过程和二进制升级更容易,也可以使潜在的代码膨胀问题最小化。 (2) 不要只是因为函数模板出现在头文件中,就将它们声明为inline。

觉得不错,请一键三连吧↓

小讯
上一篇 2025-02-15 16:51
下一篇 2025-02-11 07:29

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/35745.html