C ++ 헤더 파일에 구현이 어떻게 포함될 수 있습니까?
좋아, 어떤 식 으로든 C / C ++ 전문가는 아니지만 헤더 파일의 요점은 함수를 선언하는 것이라고 생각하고 C / CPP 파일은 구현을 정의하는 것이라고 생각했습니다.
그러나 오늘 밤 일부 C ++ 코드를 검토 한 결과 클래스의 헤더 파일에서 이것을 발견했습니다.
public:
UInt32 GetNumberChannels() const { return _numberChannels; } // <-- Huh??
private:
UInt32 _numberChannels;
그렇다면 헤더에 구현이있는 이유는 무엇입니까? const
키워드 와 관련이 있습니까? 클래스 메서드를 인라인합니까? CPP 파일에서 구현을 정의하는 것과 비교하여 이러한 방식으로 수행하는 이점 / 포인트는 정확히 무엇입니까?
좋아, 어떤 식 으로든 C / C ++ 전문가는 아니지만 헤더 파일의 요점은 함수를 선언하는 것이라고 생각하고 C / CPP 파일은 구현을 정의하는 것이라고 생각했습니다.
헤더 파일의 진정한 목적은 여러 소스 파일간에 코드를 공유하는 것입니다. 그것은됩니다 일반적으로 더 나은 코드 관리를위한 구현에서 별도의 선언에 사용하지만 요구 사항은 아닙니다. 헤더 파일에 의존하지 않는 코드를 작성할 수 있으며 헤더 파일만으로 구성된 코드를 작성할 수 있습니다 (STL 및 Boost 라이브러리가 좋은 예입니다). 때, 기억 전처리 발생 #include
문을,이 참조되는 파일의 내용으로 문을 대체 한 후 컴파일러 만 완성 된 사전 처리 된 코드를 본다.
예를 들어 다음 파일이있는 경우 :
Foo.h :
#ifndef FooH
#define FooH
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
#endif
Foo.cpp :
#include "Foo.h"
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp :
#include "Foo.h"
Foo f;
UInt32 chans = f.GetNumberChannels();
처리기는 별도로 foo.cpp에와 Bar.cpp을 분석하고 그 다음 코드를 생성하는 컴파일러는 다음 구문 분석 :
Foo.cpp :
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp :
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
Foo f;
UInt32 chans = f.GetNumberChannels();
Bar.cpp는 Bar.obj로 컴파일되며 Foo::GetNumberChannels()
. Foo.cpp는 Foo.obj로 컴파일되며 Foo::GetNumberChannels()
. 컴파일 후 링커 는 .obj 파일을 일치시키고 함께 연결하여 최종 실행 파일을 생성합니다.
그렇다면 헤더에 구현이있는 이유는 무엇입니까?
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.
Does it have to do with the const keyword?
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.
What exactly is the benefit/point of doing it this way vs. defining the implementation in the CPP file?
When used effectively, it allows the compiler to usually produce faster and better optimized machine code.
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).
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.
It is implicitly declared inline
by virtue of being a member function defined within the class declaration. This does not mean the compiler has to 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.
If it were a non-member function, then you would have to explicitly declare it as inline
:
inline void foo() { std::cout << "foo!\n"; }
* See here for more on const
at the end of a member function.
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.
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.
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.
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.
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 immutable for the duration of the method call.
C++ standard quotes
The C++17 N4659 standard draft 10.1.6 "The inline specifier" says that methods are implicitly inline:
4 A function defined within a class definition is an inline function.
and then further down we see that inline methods not only can, but must be defined on all translation units:
6 An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (6.2).
This is also explicitly mentioned in a note at 12.2.1 "Member functions":
1 A member function may be defined (11.4) in its class definition, in which case it is an inline member function (10.1.6) [...]
3 [ Note: There can be at most one definition of a non-inline member function in a program. There may be more than one inline member function definition in a program. See 6.2 and 10.1.6. — end note ]
GCC 8.3 implementation
main.cpp
struct MyClass {
void myMethod() {}
};
int main() {
MyClass().myMethod();
}
Compile and view symbols:
g++ -c main.cpp
nm -C main.o
output:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
U __stack_chk_fail
0000000000000000 T main
then we see from man nm
that the MyClass::myMethod
symbol is marked as weak on the ELF object files, which implies that it can appear on multiple object files:
"W" "w" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the symbol is determined in a system-specific manner without error. On some systems, uppercase indicates that a default value has been specified.
참고URL : https://stackoverflow.com/questions/14517546/how-can-a-c-header-file-include-implementation
'program story' 카테고리의 다른 글
디버깅 할 때 COM 구성 요소 VS2012 호출에서 오류 HRESULT E_FAIL이 반환되었습니다. (0) | 2020.12.07 |
---|---|
TFS 소스 제어 바인딩 다시 설정 (0) | 2020.12.07 |
ADT 업데이트 후 ClassNotFoundException (0) | 2020.12.07 |
Kotlin의 게터 및 세터 (0) | 2020.12.07 |
Mercurial 저장소 기록에서 삭제 된 파일을 신속하게 찾으십니까? (0) | 2020.12.07 |