如果你只是学习语言,你真的不应该担心这个。考虑它足够快,直到证明不是这样。也就是说,这里有很多误导性或不完整的答案,所以为了记录,我将充实一些更微妙的含义。考虑你的班级:
class Booth
{
public:
int get_tickets_sold();
void set_tickets_sold();
private:
int tickets_sold;
};
get 和 set 函数的实现(称为定义)尚未指定。如果你在类声明中指定了函数体,那么编译器会认为你已经隐式地要求它们被内联(但如果它们太大可能会忽略它)。如果您稍后使用inline
关键字指定它们,那将具有完全安全的效果。总结...
class Booth
{
public:
int get_tickets_sold() { return tickets_sold; }
...
...和...
class Booth
{
public:
int get_tickets_sold();
...
};
inline int Booth::get_tickets_sold() { return tickets_sold; }
...是等价的(至少在标准鼓励我们期望的方面,但个别编译器启发式可能会有所不同 - 内联是编译器可以自由忽略的请求)。
如果函数体稍后在没有inline
关键字的情况下指定,则编译器没有义务内联它们,但仍然可以选择这样做。如果它们出现在同一个翻译单元中(即在您正在编译的 .cc/.cpp/.c++/etc.“实现”文件中或它直接或间接包含的某些头文件中),则更有可能这样做。 如果实现只在链接时可用,那么函数可能根本没有内联,但这取决于您的特定编译器和链接器交互和协作的方式。这不仅仅是启用优化和期待魔法的问题。为了证明这一点,请考虑以下代码:
// inline.h:
void f();
// inline.cc:
#include <cstdio>
void f() { printf("f()\n"); }
// inline_app.cc:
#include "inline.h"
int main() { f(); }
构建这个:
g++ -O4 -c inline.cc
g++ -O4 -o inline_app inline_app.cc inline.o
调查内联:
$ gdb inline_app
...
(gdb) break main
Breakpoint 1 at 0x80483f3
(gdb) break f
Breakpoint 2 at 0x8048416
(gdb) run
Starting program: /home/delroton/dev/inline_app
Breakpoint 1, 0x080483f3 in main ()
(gdb) next
Single stepping until exit from function main,
which has no line number information.
Breakpoint 2, 0x08048416 in f ()
(gdb) step
Single stepping until exit from function _Z1fv,
which has no line number information.
f()
0x080483fb in main ()
(gdb)
请注意,执行从 main() 中的 0x080483f3 到 f() 中的 0x08048416,然后回到 main() 中的 0x080483fb ......显然没有内联。这说明不能仅仅因为函数的实现微不足道而期望内联。
请注意,此示例使用目标文件的静态链接。显然,如果您使用库文件,您实际上可能希望避免专门对函数进行内联,以便您可以更新库而无需重新编译客户端代码。对于无论如何在加载时隐式完成链接的共享库,它甚至更有用。
很多时候,提供普通函数的类使用两种形式的预期内联函数定义(即在类内或 withinline
关键字),如果这些函数可以预期在任何性能关键循环内被调用,但相反的考虑是通过内联一个您强制客户端代码重新编译(相对较慢,可能没有自动触发器)和重新链接(快速,因为共享库在下次执行时发生),而不是仅仅重新链接,以便获取对函数实现的更改。
这些考虑很烦人,但是对这些权衡的刻意管理是允许企业使用 C 和 C++ 扩展到数以亿计的行和数千个单独的项目,几十年来所有这些项目都共享各种库。
另一个小细节:作为一个大概的数字,外联 get/set 函数通常比等效的内联代码慢一个数量级 (10x)。这显然会因 CPU、编译器、优化级别、变量类型、缓存命中/未命中等而有所不同。