C++ 头文件如何包含实现?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14517546/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can a C++ header file include implementation?
提问by Mark A. Donohoe
Ok, not a C/C++ expert by any means, but I thought the point of a header file was to declare the functions, then the C/CPP file was to define the implementation.
好吧,无论如何都不是 C/C++ 专家,但我认为头文件的重点是声明函数,然后 C/CPP 文件是定义实现。
However, reviewing some C++ code tonight, I found this in a class's header file...
然而,今晚回顾了一些 C++ 代码,我在一个类的头文件中发现了这个......
public:
UInt32 GetNumberChannels() const { return _numberChannels; } // <-- Huh??
private:
UInt32 _numberChannels;
So why is there an implementation in a header? Does it have to do with the const
keyword? Does that inline a class method? What exactly is the benefit/point of doing it this way vs. defining the implementation in the CPP file?
那么为什么在标题中有一个实现呢?跟const
关键字有关系吗?这是否内联类方法?与在 CPP 文件中定义实现相比,这样做的好处/要点究竟是什么?
回答by Remy Lebeau
Ok, not a C/C++ expert by any means, but I thought the point of a header file was to declare the functions, then the C/CPP file was to define the implementation.
好吧,无论如何都不是 C/C++ 专家,但我认为头文件的重点是声明函数,然后 C/CPP 文件是定义实现。
The true purpose of a header file is to share code amongst multiple source files. It is commonlyused to separate declarations from implementations for better code management, but that is not a requirement. It is possible to write code that does not rely on header files, and it is possible to write code that is made up of just header files (the STL and Boost libraries are good examples of that). Remember, when the preprocessorencounters an #include
statement, it replaces the statement with the contents of the file being referenced, then the compileronly sees the completed pre-processed code.
头文件的真正目的是在多个源文件之间共享代码。它通常用于将声明与实现分开以实现更好的代码管理,但这不是必需的。可以编写不依赖于头文件的代码,也可以编写仅由头文件组成的代码(STL 和 Boost 库就是很好的例子)。请记住,当预处理器遇到#include
语句时,它会用被引用文件的内容替换该语句,然后编译器只能看到已完成的预处理代码。
So, for example, if you have the following files:
因此,例如,如果您有以下文件:
Foo.h:
福.h:
#ifndef FooH
#define FooH
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
#endif
Foo.cpp:
文件.cpp:
#include "Foo.h"
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp:
酒吧.cpp:
#include "Foo.h"
Foo f;
UInt32 chans = f.GetNumberChannels();
The preprocessorparses Foo.cpp and Bar.cpp separately and produces the following code that the compilerthen parses:
该预处理器解析Foo.cpp中和Bar.cpp分开,并产生如下代码,该编译器然后分析:
Foo.cpp:
文件.cpp:
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp:
酒吧.cpp:
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
Foo f;
UInt32 chans = f.GetNumberChannels();
Bar.cpp compiles into Bar.obj and contains a reference to call into Foo::GetNumberChannels()
. Foo.cpp compiles into Foo.obj and contains the actual implementation of Foo::GetNumberChannels()
. After compiling, the linkerthen matches up the .obj files and links them together to produce the final executable.
Bar.cpp 编译成 Bar.obj 并包含调用到Foo::GetNumberChannels()
. Foo.cpp 编译成 Foo.obj 并包含Foo::GetNumberChannels()
. 编译后,链接器然后匹配 .obj 文件并将它们链接在一起以生成最终的可执行文件。
So why is there an implementation in a header?
那么为什么在标题中有一个实现呢?
By including the method implementation inside the method declaration, it is being implicitly declared as inlined (there is an actual inline
keyword that can be explicitly used as well). Indicating that the compiler should inline a function is only a hint which does not guarantee that the function will actually get inlined. But if it does, then wherever the inlined function is called from, the contents of the function are copied directly into the call site, instead of generating a CALL
statement to jump into the function and jump back to the caller upon exiting. The compiler can then take the surrounding code into account and optimize the copied code further, if possible.
通过在方法声明中包含方法实现,它被隐式声明为内联(也inline
可以显式使用一个实际的关键字)。指示编译器应该内联函数只是一个提示,并不能保证该函数实际上会被内联。但是如果是这样,那么无论从哪里调用内联函数,函数的内容都会直接复制到调用点,而不是生成一个CALL
语句跳转到函数并在退出时跳回调用者。如果可能,编译器然后可以考虑周围的代码并进一步优化复制的代码。
Does it have to do with the const keyword?
它与 const 关键字有关吗?
No. The const
keyword merely indicates to the compiler that the method will not alter the state of the object it is being called on at runtime.
否。const
关键字仅向编译器指示该方法不会在运行时更改它正在调用的对象的状态。
What exactly is the benefit/point of doing it this way vs. defining the implementation in the CPP file?
与在 CPP 文件中定义实现相比,这样做的好处/要点究竟是什么?
When used effectively, it allows the compiler to usually produce faster and better optimized machine code.
如果使用得当,它通常允许编译器生成更快、更好优化的机器代码。
回答by Agentlien
It is perfectly valid to have an implementation of a function in a header file. The only issue with this is breaking the one-definition-rule. That is, if you include the header from multiple other files, you will get a compiler error.
在头文件中实现函数是完全有效的。唯一的问题是打破单一定义规则。也就是说,如果您包含来自多个其他文件的标头,则会出现编译器错误。
However, there is one exception. If you declare a function to be inline, it is exempt from the one-definition-rule. This is what is happening here, since member functions defined inside a class definition are implicitly inline.
但是,有一个例外。如果您将函数声明为内联函数,则它不受单一定义规则的约束。这就是这里发生的事情,因为在类定义中定义的成员函数是隐式内联的。
Inline itself is a hint to the compiler that a function may be a good candidate for inlining. That is, expanding any call to it into the definition of the function, rather than a simple function call. This is an optimization which trades the size of the generated file for faster code. In modern compilers, providing this inlining hint for a function is mostly ignored, except for the effects it has on the one-definition-rule. Also, a compiler is always free to inline any function it sees fit, even if it has not been declared inline
(explicitly or implicitly).
内联本身是对编译器的一个提示,即一个函数可能是内联的良好候选者。也就是说,将对其的任何调用扩展到函数的定义中,而不是简单的函数调用。这是一种优化,它以生成文件的大小换取更快的代码。在现代编译器中,为函数提供这种内联提示通常被忽略,除了它对单一定义规则的影响。此外,编译器始终可以自由地内联它认为合适的任何函数,即使它尚未声明inline
(显式或隐式)。
In your example, the use of const
after the argument list signals that the member function does not modify the object on which it is called. In practice, this means that the object pointed to by this
, and by extension all class members, will be considered const
. That is, trying to modify them will generate a compile-time error.
在您的示例中,使用const
after 参数列表表示成员函数不会修改调用它的对象。在实践中,这意味着 指向的对象this
以及扩展所有类成员都将被考虑const
。也就是说,尝试修改它们会产生编译时错误。
回答by juanchopanza
It is implicitly declaredinline
by virtue of being a member function definedwithin the class declaration. This does not mean the compiler hasto inline it, but it means you won't break the one definition rule. It is completely unrelated to const
*. It is also unrelated to the length and complexity of the function.
它是隐式声明inline
由于是一个成员函数定义的类声明中。这并不意味着编译器必须内联它,而是意味着您不会破坏一个定义规则。它与const
*完全无关。它也与函数的长度和复杂性无关。
If it were a non-member function, then you would have to explicitly declare it as inline
:
如果它是一个非成员函数,那么您必须将其显式声明为inline
:
inline void foo() { std::cout << "foo!\n"; }
*See herefor more on const
at the end of a member function.
*有关成员函数末尾的更多信息,请参见此处const
。
回答by steveha
Even in plain C, it is possible to put code in a header file. If you do it, you usually need to declare it static
or else multiple .c files including the same header will cause a "multiply defined function" error.
即使在普通 C 中,也可以将代码放入头文件中。如果这样做,通常需要声明它static
,否则包含相同标头的多个 .c 文件将导致“多重定义函数”错误。
The preprocessor textually includes an include file, so the code in an include file becomes part of the source file (at least from the compiler's point of view).
预处理器以文本方式包含一个包含文件,因此包含文件中的代码成为源文件的一部分(至少从编译器的角度来看)。
The designers of C++ wanted to enable object-oriented programming with good data hiding, so they expected to see lots of getter and setter functions. They didn't want an unreasonable performance penalty. So, they designed C++ so that the getters and setters could not only be declared in the header but actually implemented, so they would inline. That function you showed is a getter, and when that C++ code is compiled, there won't be any function call; code to fetch out that value will just be compiled in place.
C++ 的设计者希望通过良好的数据隐藏实现面向对象编程,因此他们希望看到大量的 getter 和 setter 函数。他们不想要不合理的性能惩罚。因此,他们设计了 C++,这样 getter 和 setter 不仅可以在头文件中声明,而且可以实际实现,因此它们可以内联。你展示的那个函数是一个getter,当编译那个C++代码时,不会有任何函数调用;提取该值的代码将被编译到位。
It is possible to make a computer language that doesn't have the header file/source file distinction, but just has actual "modules" that the compiler understands. (C++ didn't do that; they just built on top of the successful C model of source files and textually included header files.) If source files are modules, it would be possible for a compiler to pull code out of the module and then inline that code. But the way C++ did it is simpler to implement.
可以制作一种没有头文件/源文件区别的计算机语言,但只有编译器可以理解的实际“模块”。(C++ 没有这样做;它们只是建立在成功的源文件和文本包含的头文件的 C 模型之上。)如果源文件是模块,编译器就有可能从模块中提取代码,然后内联该代码。但是 C++ 的方式实现起来更简单。
回答by Spook
As far as I know, there are two kinds of methods, which can be safely implemented inside the header file.
据我所知,有两种方法,可以安全地在头文件内部实现。
- Inline methods - their implementation is copied to places, where they are used, so there is no problem with double-definition linker errors;
- Template methods - they are actually compiled at the moment of template instantiation (eg. when someone inputs a type in place of template), so again there is no possibility of double-definition problem.
- 内联方法——它们的实现被复制到使用它们的地方,所以没有双定义链接器错误的问题;
- 模板方法 - 它们实际上是在模板实例化的那一刻编译的(例如,当有人输入一个类型来代替模板时),所以同样没有双重定义问题的可能性。
I believe, your example fits the first case.
我相信,你的例子适合第一种情况。
回答by Jonas Bystr?m
Keeping the implementation in the class header file works, as I'm sure you know if you compiled your code. The const
keyword ensures you don't change any members, it keeps the instance immutablefor the duration of the method call.
将实现保留在类头文件中是可行的,因为我确定您是否知道编译了代码。该const
关键字可确保您不会改变任何成员,它使实例不变的方法调用的持续时间。