只是我的两分钱。这不是特别关于虚函数,而是关于内联函数和成员函数。也许它是有用的。
C++
就标准 C++ 而言,必须在使用它的每个翻译单元中定义内联函数。并且非静态内联函数将在每个翻译单元中具有相同的静态变量和相同的地址。编译器/链接器必须将多个定义合并到一个函数中才能实现这一点。因此,始终将内联函数的定义放在头文件中 - 如果您仅在实现文件(“.cpp”)(对于非成员函数)中定义它,则不要将其声明放入头文件中,因为如果您如果有人使用它,您会收到有关未定义函数或类似内容的链接器错误。
这与必须在整个程序中仅定义一次的非内联函数不同(one-definition-rule)。对于内联函数,如上所述的多个定义是相当正常的情况。这与调用是否最终内联无关。关于内联函数的规则仍然很重要。Microsoft 编译器是否遵守这些规则 - 我不能告诉你。如果它在这方面遵守标准,那么它会。但是,我可以想象一些使用虚拟、dll 和不同 TU 的组合可能会出现问题。我从未测试过它,但我相信没有问题。
对于成员函数,如果你在类中定义你的函数,它是隐式内联的。由于它出现在标题中,因此必须在使用它的每个翻译单元中定义它的规则会自动得到满足。但是,如果您在类外和头文件中定义函数(例如,因为中间有代码的循环依赖),那么如果您多次包含相应的文件,则该定义必须是内联的,以避免链接器引发的多定义错误。文件示例f.h
:
struct f {
// inline required here or before the definition below
inline void g();
};
void f::g() { ... }
这与将定义直接放入类定义中具有相同的效果。
C99
请注意,C99 的内联函数规则比 C++ 更复杂。这里,内联函数可以定义为内联定义,在整个程序中可以存在多个。但是如果使用这样的(内联)定义(例如,如果它被调用),那么在另一个翻译单元中包含的整个程序中也必须有一个外部定义。基本原理(引自解释几个 C99 特性背后基本原理的 PDF):
C99 中的内联确实以两种方式扩展了 C++ 规范。首先,如果一个函数在一个翻译单元中被声明为内联,则不需要在每个其他翻译单元中都被声明为内联。例如,这允许在库中内联但只能通过其他地方的外部定义使用的库函数。为外部函数使用包装函数的替代方法需要一个额外的名称;如果翻译器实际上不进行内联替换,它也可能对性能产生不利影响。
其次,内联函数的所有定义“完全相同”的要求被以下要求取代:程序的行为不应依赖于调用是用可见的内联定义还是外部定义来实现的。功能。这允许内联定义专门用于在特定翻译单元中使用。例如,库函数的外部定义可能包括一些参数验证,这些参数验证对于从同一库中的其他函数进行的调用来说是不需要的。这些扩展确实提供了一些优势;关心兼容性的程序员可以简单地遵守更严格的 C++ 规则。
为什么我将 C99 包含在此处?因为我知道微软编译器支持 C99 的一些东西。所以在那些 MSDN 页面中,一些东西也可能来自 C99——虽然没有特别想出任何东西。在阅读它并将这些技术应用于自己的旨在成为可移植 C++ 的 C++ 代码时,应该小心。可能会告知哪些部分是 C99 特定的,哪些不是。
测试 C++ 小片段是否符合标准的好地方是comeau 在线编译器。如果它被拒绝,可以很确定它不是严格符合标准的。