第一周的课程中,老师提到,使用inline定义函数会提高效率,当时只是简单的记下了。这周作业中,下意识的,在Rectangle.h文件中声明一个类:
然后在对应的cpp文件中添加实现,按照老师的建议,在所有不复杂的定义之前,都添加了一个inline关键字:
编译没有问题,perfect!
接下来写测试代码,调用Rectangle的构造,然后问题就来了:
错误提示很明显,函数未定义。可是,之前有定义,而且在调用之前的编译都没有问题,什么鬼?!
接下来逐步调整代码(都是简单的步骤,具体过程就不啰嗦了),发现了规律:
方式一:
1. rectangle.h中声明成员函数,不加inline;
2. rectangle.cpp中实现,加inline;
3. main()中调用,报错,不调用,没有问题。(这里的main()不在rectangle.cpp中,下同);
方式二:
1. rectangle.h中声明成员函数,加inline;
2. rectangle.cpp中实现,不加inline;
3. main()中调用,报错,不调用,没有问题。
方式三:
1. rectangle.h中声明成员函数,不加inline;
2. rectangle.cpp中实现,不加inline;
3. main()中调用,没有问题。
方式四:
1. rectangle.h中声明成员函数,不加inline;
2. rectangle.h中实现,加inline;
3. main()中调用,没有问题。
反复验证后得出结论,inline function的定义,需要写在相应的头文件中。回过头去翻看了课程视频,老师似乎也没有提到这一点。
为了确认这个问题,找了c++11标准,找到里边关于inline function的说明:
An inline function shall be defined in every translation unit in which it is odr-used.
内联函数需要在每个用到它的编译单元定义。
A function declaration (8.3.5, 9.3, 11.3) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism(每个调用到内联函数的地方,都会替换成内联函数的函数体). An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by 7.1.2 shall still be respected.
A function defined within a class definition is an inline function. The inline specifier shall not appear on a block scope function declaration.91 If the inline specifier is used in a friend declaration, that declaration shall be a definition or the function shall have previously been declared inline.
An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (3.2).(内联函数必须在每个用到的编译单元定义,且每个定义需要相同) [Note: A call to the inline function may be encountered before its definition appears in the translation unit. —end note] If the definition of a function appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. An inline function with external linkage shall have the same address in all translation units. A static local variable in an extern inline function always refers to the same object. A string literal in the body of an extern inline function is the same object in different translation units. [Note: A string literal appearing in a default argument is not in the body of an inline function merely because the expression is used in a function call from that inline function. —end note] A type defined within the body of an extern inline function is the same type in every translation unit.
与此相关的描述,已经在引文中添加了翻译,就是一条:需要在每个用到的编译单元做出定义,且每个定义需要保持一致。
看到这里,又做了一次尝试:
方式五:
1. rectangle.h中声明成员函数,不加inline;
2. rectangle.cpp中实现,加inline;
2. test.cpp中添加同样的实现,加inline;
3. main()中调用,没有问题。
这种方法虽然可行,但是存在一个弊端,工程项目不断添加新的文件,很难保证inline在每个.c文件中都能定义相同,所以并不可取。
综上所述,我们使用inline的时候,还是应该在头文件的声明中,直接写出函数的定义,non-inline函数的定义,则写到对应的cpp文件中,提高程序可读性和可维护性。
PS. 今天周末,难得有空,回顾了一下这篇笔记,又针对incline function的研究查了些资料,果然发现更多精彩。在这个这个链接 中,发现了前辈对这一问题的讨论。
"But putting function bodies at the end of my header is ugly. Isn't there a way to avoid that?"
Yep! Just move the function bodies to another header:
果然,代码更清晰,避免了circular reference的同时,也让代码更加优雅。值得学习。