The role of C++ header files (in-depth understanding of the role of header files and source files in C++)

Recently, C++ is used in the core battle of the project. I have only read related books since I started working. The teachers in the university have taught me a lot of basic knowledge. I hope I can take this opportunity to pick up C++ and add another skill in the future. The following articles come from the Internet and are organized according to my own understanding. I recorded them only for taking notes for my own convenience.

 

the difference

Let me talk about the conclusion first:

        The .h file can contain:
  the declaration of class member data, but cannot assign
  the definition and assignment of class static data members, but it is not recommended, just a declaration is fine.
  Declaration of class member functions
  Declaration of non-class member functions
  Definition of constants: such as: constant a=5;
  definition of static functions
  Definition of inline functions of classes
  Cannot include:
  1. Declaration 2 of all non-static variables (not data members of a class)
  . The default namespace declaration should not be placed in the header file, using namespace std; etc. should be placed in .cpp, and std::string should be used in the .h file

1. How to associate source files with header files according to #include
  1. The header files that come with the system are enclosed in angle brackets, so that the compiler will search in the system file directory.
  2. User-defined files are enclosed in double quotes. The compiler will first search in the user directory, and then go to the C++ installation directory (for example, the library file search path can be specified and modified in VC, and
the environment variable can be used in Unix and Linux . to set), and finally in the system files.
  #include ""xxx.h" (I always thought there was no difference between "" and <>, but tinyxml.h is a non-system file, so use "") 2. The
  question
of how to associate the header file with the source file
is actually That is to say, it is known that the header file "ah" declares a series of functions, and these functions are implemented in "b.cpp", so if I want to use these functions declared in "ah" in "c.cpp" in "b. The functions implemented in "cpp" usually use #include "ah" in "c.cpp", so how does c.cpp find the implementation in b.cpp? In fact, the
  names of .cpp and .h files have no direct Relationship, many compilers can accept other extensions. For example, now I see the source code of our company, the .cpp file is replaced by the .cc file. In Turbo C, the command line is used to compile, and the command line parameters
  are The name of the file, the default is .cpp and .h, but it can also be customized as .xxx, etc.
  Mr. Tan Haoqiang’s book " C Programming " mentioned that when the compiler preprocesses, the #include command should be executed "File Include Handling": copy the entire contents of file2.c to #i nclude  "file 2.c". This also explains why many compilers don't care what the suffix of this file is - because #include preprocessing is to complete a "copy and insert code" work.
  When compiling, it will not find the function implementation in the b.cpp file, and only do this work when linking. We use #include "ah" in b.cpp or c.cpp to actually  introduce related declarations , so that the compilation can pass, the program does not care where and how it is implemented. After the source file is compiled, an object file (.o or .obj file) is generated. In the object file, these functions and variables are regarded as one A symbol. When linking , you need to specify in the makefile which .o or .obj file you need to link (here, the .o or .obj file generated by b.cpp), and the linker will go to this .o or .obj file Find the functions implemented in b.cpp in the .obj file, and then build them into the executable file specified in the makefile
.   Under Unix, you don’t even need to include the header file in the source file, you only need to name it in the makefile . Yes (but this greatly reduces the readability of the program, it is a bad habit ^_^). In VC , you don’t need to write the makefile yourself in some cases , you only need to include all the required files in the project , VC Will automatically write the makefile for you.
  Usually, the C++ compilerIt will look for the required symbols in each .o or .obj file, instead of just finding one in a certain file or not finding one. Therefore, if the same function is implemented in several different files, or the same global variable is defined, " redefined " will be prompted when linking.     above all:
  

How did the above conclusions come to be? This requires us to understand the C++ compilation mode.

1. C++ compilation mode
Usually, in a C++ program, only two types of files are included—.cpp files and .h files. Among them, .cpp files are called C++ source files, which contain C++ source code; and .h files are called C++ header files, which also contain C++ source code.
C + + language supports "separate compilation" (separate compilation). In other words, all the content of a program can be divided into different parts and placed in different .cpp files. The things in the .cpp file are relatively independent, and there is no need to communicate with other files when compiling (compile). You only need to link (link) with other target files after compiling into an object file. For example, a global function "void a(){}" is defined in file a.cpp, and this function needs to be called in file b.cpp. Even so, file a.cpp and file b.cpp do not need to know each other's existence, but they can be compiled separately, compiled into object files and then linked, and the whole program can run.
How is this possible? From a programming point of view, it is very simple. In the file b.cpp, before calling the "void a()" function, declare the function "voida();" first, and that's it. This is because the compiler will generate a symbol table (symbol table) when compiling b.cpp, and symbols like "void a()" that cannot be seen and defined will be stored in this table. When linking again, the compiler will look for the definition of this symbol in other object files. Once found, the program can be successfully generated.
Note that two concepts are mentioned here, one is "definition" and the other is "declaration". Simply put, "definition" is to describe a symbol completely: whether it is a variable or a function, what type it returns, what parameters it needs, and so on. The "declaration" just declares the existence of this symbol, that is, tells the compiler that this symbol is defined in other files, I will use it here first, and then go to other places to find out what it is when you link Bar. When defining, a symbol (variable or function) should be completely defined according to C++ syntax, and when declaring, you only need to write the prototype of this symbol. It should be noted that a symbol can be declared multiple times in the entire program, but it must be defined only once. Just imagine, if there are two different definitions of a symbol, who should the compiler listen to?
This mechanism brings many benefits to C++ programmers, and it also leads to a way to write programs. Think about it, if there is a very commonly used function "void f() {}", which will be called in many .cpp files throughout the program, then we only need to define this function in one file, and in other Just declare this function in the file. A function is easy to deal with, and it only needs one sentence to declare it. However, what if there are too many functions, such as a large number of mathematical functions, hundreds of them? Can it be guaranteed that every programmer can completely and accurately write down and write down the forms of all functions?


Second, what is a header file
Obviously, the answer is impossible. But there is a very simple way that can help programmers save the trouble of remembering so many function prototypes: we can write all the declaration statements of those hundreds of functions first, put them in a file, and wait until the programmer needs to When they are, copy all these things into his source code.
This method is certainly feasible, but it is still too troublesome, and it seems very clumsy. Therefore, the header file can play its role. The so-called header file, in fact, its content is the same as the content in the .cpp file, which is the source code of C++. But header files don't have to be compiled. We put all the function declarations into a header file. When a .cpp source file needs them, they can be included into this .cpp file through a macro command "#include" to merge their contents. to the .cpp file. These .h files are included when the .cpp file is compiled.
To give an example, suppose there are only two mathematical functions: f1 and f2, then we put their definitions in math.cpp:
/* math.cpp */
double f1()
{     //do something here.. ..     return; } double f2(double a) {     //do something here...     return a * a; } /* end of math.cpp */ and put the declaration of "these" functions in a header file math. h: /* math.h */ double f1();












double f2(double);
/* end of math.h */
In another file main.cpp, I want to call these two functions, so I just need to include the header file:
/* main.cpp */
#include "math.h"
main()
{     int number1 = f1();     int number2 = f2(number1); } /* end of main.cpp */ In this way, it is a complete program. It should be noted that the .h file does not need to be written after the compiler command, but it must be in a place where the compiler can find it (for example, in the same directory as main.cpp). Both main.cpp and math.cpp can be passed through Compile, generate main.o and math.o, and then link these two object files, the program can run.





3. #include
#include is a macro command from the C language, it will work before the compiler compiles, that is, during precompilation. The function of #include is to include the content of the file written behind it into the current file completely and word by word. It is worth mentioning that it does not have any other functions or side functions. Its function is to replace every place where it appears with the content of the file written behind it. Simple text replacement and nothing else. Therefore, the first sentence (#include "math.h") in the main.cpp file will be replaced with the content of the math.h file before compilation. That is, when the compilation process is about to start, the content of main.cpp has changed:
/* ~main.cpp */
double f1();
double f2(double);
main()
{     int number1 = f1();     int number2 = f2(number1); } /* end of ~main.cpp */ No more, no less, just right. In the same way, if we have many .cpp files other than main.cpp that also use the f1 and f2 functions, then they all need to write #include "math. h" will do.





4. What should be written in the header file
Through the above discussion, we can understand that the function of the header file is to be included by other .cpp. They don't take part in compilation themselves, but their contents are actually compiled in multiple .cpp files. Through the rule of "there can only be one definition", we can easily conclude that only variable and function declarations should be placed in the header file, not their definitions. Because the content of a header file will actually be introduced into multiple different .cpp files, and they will all be compiled. Of course it’s okay to put the declaration. If you put the definition, it is equivalent to the definition of a symbol (variable or function) in multiple files. Even though these definitions are the same, for the compiler, doing so illegal.
Therefore, one thing that should be remembered is that in the .h header file, there can only be variable or function declarations, not definitions. That is, you can only write sentences such as: extern int a; and void f(); in the header file. These are the statements. If you write a sentence like inta; or void f() {}, then once this header file is included by two or more .cpp files, the compiler will report an error immediately. (About extern, it has been discussed before, and the difference between definition and declaration will not be discussed here.)
However, there are three exceptions to this rule.
First, the definition of const objects can be written in the header file. Because the global const object has no extern declaration by default, it is only valid in the current file. Write such an object into the header file, even if it is included in other multiple . would result in multiple definitions. At the same time, because the objects in these .cpp files are all included from a header file, this ensures that the values ​​of the const objects in these .cpp files are the same, which can be said to kill two birds with one stone. Similarly, the definition of static objects can also be put into the header file.
Second, the definition of inline function (inline) can be written in the header file. Because the inline function requires the compiler to expand it inline according to its definition when it encounters it, it is not a normal function that can be declared first and then linked (inline functions will not be linked), so the compiler needs See the full definition of the inline function at compile time. This is difficult if inline functions can only be defined once like ordinary functions. Because it’s okay in a file, I can write the definition of the inline function at the beginning, so that I can see the definition when I use it later; but, what if I use this function in other files? What to do? There is almost no good solution to this, so C++ stipulates that inline functions can be defined multiple times in a program, as long as the inline function only appears once in a .cpp file, and in all .cpp files, this inline The definition of the function is the same, and it can be compiled. Obviously, it is very wise to put the definition of the inline function into a header file.
Third, the definition of a class can be written in the header file. Because when creating an object of a class in a program, the compiler can only know how the object of this class should be laid out when the definition of this class is fully visible. Therefore, the requirements for class definition are basically the same as inline functions. the same. So it is a good practice to put the definition of the class into the header file and include the header file in the .cpp file that uses this class. Here, it is worth mentioning that the class definition contains data members and function members. Data members are not defined (allocated space) until a specific object is created, but function members need to be defined at the beginning, which is what we usually call the implementation of a class. Generally, our approach is to put the definition of the class in the header file, and put the implementation code of the function member in a .cpp file. This is possible and a very good way. However, there is another way. That is to directly write the implementation code of the function member into the class definition. In a C++ class, if a function member is defined in the body of the class definition, the compiler will treat the function as inline. Therefore, it is legal to write the definition of function members into the body of the class definition and put them together in the header file. Note that if the definition of a function member is written in the header file of the class definition, but not in the class definition, this is illegal, because the function member is not inline at this time. Once the header file is included by two or more .cpp files, the function member is redefined.


5. Protection measures in header files
Think about it, if the header file only contains statement statements, it will not matter how many times it is included in the same .cpp file-because the appearance of statement statements is unlimited. However, the three exceptions to the header files discussed above are also a very common use of header files. Then, once any of the above three exceptions appears in a header file, and it is included multiple times by a .cpp, the problem will be big. Because although the grammatical elements in these three exceptions "can be defined in multiple source files", they "can only appear once in a source file". Imagine that if ah contains the definition of class A, and bh contains the definition of class B, since the definition of class B depends on class A, ah is also #included in bh. Now there is a source file that uses both class A and class B, so the programmer includes both ah and bh in this source file. At this time, the problem comes: the definition of class A appears twice in this source file! So the whole program cannot be compiled. You might think that this is the programmer's mistake - he should know that bh contains ah - but in fact he should not know.
Using "#define" with conditional compilation can solve this problem well. In a header file, define a name through #define, and compile conditional #ifndef...#endif so that the compiler can decide whether to continue compiling the subsequent content in the header according to whether the name is defined or not. Although this method is simple, you must remember to write it in when writing the header file.

 

 

 

 

Guess you like

Origin blog.csdn.net/gtncwy/article/details/112802759