我是靠谱客的博主 唠叨饼干,最近开发中收集的这篇文章主要介绍【C++基础 01】 内联函数内联函数内联函数与宏把内联函数放入头文件慎⽤内联UE5 中对于内联函数的代码规范,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

https://wenku.baidu.com/view/1a8edaae6629647d27284b73f242336c1eb930f8.html

内联函数

在C++中我们通常定义以下函数来求两个整数的最⼤值:

代码如下:

int max(int a, int b)
{
return a > b ? a : b;
}

为这么⼀个⼩的操作定义⼀个函数的好处有:
① 阅读和理解函数 max 的调⽤,要⽐读⼀条等价的条件表达式并解释它的含义要容易得多
② 如果需要做任何修改,修改函数要⽐找出并修改每⼀处等价表达式容易得多
③ 使⽤函数可以确保统⼀的⾏为,每个测试都保证以相同的⽅式实现
④ 函数可以重⽤,不必为其他应⽤程序重写代码
虽然有这么多好处,但是写成函数有⼀个潜在的缺点:调⽤函数⽐求解等价表达式要慢得多。在⼤多数的机器上,调⽤函数都要做很多⼯作:调⽤前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向⼀个新位置执⾏
C++中⽀持内联函数,其⽬的是为了提⾼函数的执⾏效率,⽤关键字 inline 放在函数定义(注意是定义⽽⾮声明,下⽂继续讲到)的前⾯即可
将函数指定为内联函数,内联函数通常就是将它在程序中的每个调⽤点上“内联地”展开,假设我们将 max 定义为内联函数:

inline int max(int a, int b)
{
return a > b ? a : b;
}

则调⽤: cout<<max(a, b)<<endl;
在编译时展开为: cout<<(a > b ? a : b)<<endl;
从⽽消除了把 max写成函数的额外执⾏开销

内联函数与宏

⽆论是《Effective C++》中的 “Prefer consts,enums,and inlines to #defines” 条款,还是《⾼质量程序设计指南——C++/C语⾔》中的“⽤函数内联取代宏”,宏在C++中基本是被废了

把内联函数放入头文件

关键字 inline 必须与函数定义体放在⼀起才能使函数成为内联,仅将 inline 放在函数声明前⾯不起任何作⽤。

如下风格的函数 Foo 不能成为内联函数:

错误的代码如下:

inline void Foo(int x, int y);
// inline 仅与函数声明放在⼀起

void Foo(int x, int y)
{
...
}

⽽如下风格的函数 Foo 则成为内联函数:
正确的代码如下:

void Foo(int x, int y);
inline void Foo(int x, int y)
// inline 与函数定义体放在⼀起 
{
...
}

所以说,C++ inline函数是⼀种“⽤于实现的关键字”,⽽不是⼀种“⽤于声明的关键字”。⼀般地,⽤户可以阅读函数的声明,但是看不到函数的定义。尽管在⼤多数教科书中内联函数的声明、定义体前⾯都加了 inline 关键字,但我认为 inline 不应该出现在函数的声明中。

这个细节虽然不会影响函数的功能,但是体现了⾼质量C++/C 程序设计风格的⼀个基本原则:声明与定义不可混为⼀谈,⽤户没有必要、也不应该知道函数是否需要内联。

更为严格地说,内联函数不应该有声明,应该将函数定义放在本应该出现函数声明的地方,这是一种良好的编程风格。

定义在类声明之中的成员函数将⾃动地成为内联函数,例如:
代码如下:

class A
{
public:
void Foo(int x, int y) { ... }
// ⾃动地成为内联函数

}

但是编译器是否将它真正内联则要看 Foo函数如何定义

内联函数应该在头⽂件中定义,这⼀点不同于其他函数。编译器在调⽤点内联展开函数的代码时,必须能够找到 inline 函数的定义才能将调⽤函数替换为函数代码,⽽对于在头⽂件中仅有函数声明是不够的。

当然内联函数定义也可以放在源⽂件中,但此时只有定义的那个源⽂件可以⽤它,⽽且必须为每个源⽂件拷贝⼀份定义(即每个源⽂件⾥的定义必须是完全相同的),当然即使是放在头⽂件中,也是对每个定义做⼀份拷贝,只不过是编译器替你完成这种拷贝罢了。但相⽐于放在源⽂件中,放在头⽂件中既能够确保调⽤函数是定义是相同的,⼜能够保证在调⽤点能够找到函数定义从⽽完成内联(替换)。

但是你会很奇怪,重复定义那么多次,不会产⽣链接错误?

我们来看⼀个例⼦:

A.h :

代码如下:

class A
{
public:
A(int a, int b) : a(a),b(b){}
int max();
private:
int a;
int b;
};

A.cpp :

代码如下:

#include "A.h"
inline int A::max()
{
return a > b ? a : b;
}

Main.cpp :

代码如下:

#include <iostream> 
#include "A.h" 
using namespace std;
inline int A::max()
{
return a > b ? a : b;
}
int main()
{
A a(3, 5);
cout<<a.max()<<endl;
return 0;
}

⼀切正常编译,输出结果:5

倘若你在Main.cpp中没有定义max内联函数,那么会出现链接错误:

error LNK2001: unresolved external symbol “public: int __thiscall A::max(void)” (?max@A@@QAEHXZ)main.obj
找不到函数的定义,所以内联函数可以在程序中定义不⽌⼀次,只要 inline 函数的定义在某个源⽂件中只出现⼀次,⽽且在所有源⽂件中,其定义必须是完全相同的就可以。

在头⽂件中加⼊或修改 inline 函数时,使⽤了该头⽂件的所有源⽂件都必须重新编译。

关刀我使用VS2019进行编译验证,和上文中的结果是一样的。

慎⽤内联

内联虽有它的好处,但是也要慎⽤,以下摘⾃《⾼质量程序设计指南——C++/C语⾔》:

⽽在Google C++编码规范中则规定得更加明确和详细:

内联函数:

Tip: 只有当函数只有 10 ⾏甚⾄更少时才将其定义为内联函数.

  • 定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, ⽽不是按通常的函数调⽤机制进⾏调⽤.

  • 优点: 当函数体⽐较⼩的时候, 内联该函数可以令⽬标代码更加⾼效. 对于存取函数以及其它函数体⽐较短, 性能关键的函数, ⿎励使⽤内联.

  • 缺点: 滥⽤内联将导致程序变慢. 内联可能使⽬标代码量或增或减, 这取决于内联函数的⼤⼩. 内联⾮常短⼩的存取函数通常会减少代码⼤⼩,
    但内联⼀个相当⼤的函数将戏剧性的增加代码⼤⼩. 现代处理器由于更好的利⽤了指令缓存, ⼩巧的代码往往执⾏更快。

  • 结论: ⼀个较为合理的经验准则是, 不要内联超过 10 ⾏的函数. 谨慎对待析构函数, 析构函数往往⽐其表⾯看起来要更长, 因为有隐含的成员和基类析构函数被调⽤!

另⼀个实⽤的经验准则: 内联那些包含循环或 switch 语句的函数常常是得不偿失 (除⾮在⼤多数情况下, 这些循环或 switch 语句从不被执⾏).

有些函数即使声明为内联的也不⼀定会被编译器内联, 这点很重要; ⽐如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(递归调⽤堆栈的展开并不像循环那么简单, ⽐如递归层数在编译时可能是未知的, ⼤多数编译器都不⽀持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个⽅便, 抑或是当作⽂档描述其⾏为, ⽐如精短的存取函数.

-inl.h⽂件:

Tip: 复杂的内联函数的定义, 应放在后缀名为 -inl.h 的头⽂件中.

内联函数的定义必须放在头⽂件中, 编译器才能在调⽤点内联展开定义. 然⽽, 实现代码理论上应该放在 .cc ⽂件中, 我们不希望 .h ⽂件中有太多实现代码, 除⾮在可读性和性能上有明显优势.

如果内联函数的定义⽐较短⼩, 逻辑⽐较简单, 实现代码放在 .h ⽂件⾥没有任何问题. ⽐如, 存取函数的实现理所当然都应该放在类定义内.
出于编写者和调⽤者的⽅便, 较复杂的内联函数也可以放到 .h ⽂件中, 如果你觉得这样会使头⽂件显得笨重, 也可以把它萃取到单独的 -inl.h中. 这样把实现和类定义分离开来, 当需要时包含对应的 -inl.h 即可。

UE5 中对于内联函数的代码规范

  • 内联函数会强制在不使用其的文件中强行编译,因此勿使用过多内联函数。内联函数仅可在浅显访问器中和分析显示有益时使用。
  • 使用 FORCEINLINE 时需更加谨慎。所有代码和本地变量将扩展至调用函数中,将导致与大型函数相同的编译时间问题。

最后

以上就是唠叨饼干为你收集整理的【C++基础 01】 内联函数内联函数内联函数与宏把内联函数放入头文件慎⽤内联UE5 中对于内联函数的代码规范的全部内容,希望文章能够帮你解决【C++基础 01】 内联函数内联函数内联函数与宏把内联函数放入头文件慎⽤内联UE5 中对于内联函数的代码规范所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(39)

评论列表共有 0 条评论

立即
投稿
返回
顶部