Cpp header and source files

Headers and Includes: Why and How

*************************************************
** 0) Introduction **
*************************************************

This article is meant to address a common newbie problem regarding failure to understand #include, headers, and source file interaction. Several good practices are outlined and explained to show how to avoid some ugly snags. Later sections get into more advanced topics (inlining and templates), so even C++ coders with some experience under their belt might benefit from a read!

If you are already familiar with the basics, feel free to skip ahead to section 4. That is where practices and design strategies are discussed.

*************************************************
** 1) Why we need header files. **
*************************************************

If you’re just starting out in C++, you might be wondering why you need to #include files and why you would want to have multiple .cpp files for a program. The reasons for this are simple:

(1) It speeds up compile time. As your program grows, so does your code, and if everything is in a single file, then everything must be fully recompiled every time you make any little change. This might not seem like a big deal for small programs (and it isn’t), but when you have a reasonable size project, compile times can take several minutes to compile the entire program. Can you imagine having to wait that long between every minor change?

Compile / wait 8 minutes / «oh crap, forgot a semicolon» / compile / wait 8 minutes / debug / compile / wait 8 minutes / etc

(2) It keeps your code more organized. If you seperate concepts into specific files, it’s easier to find the code you are looking for when you want to make modifications (or just look at it to remember how to use it and/or how it works).

(3) It allows you to separate interface from implementation. If you don’t understand what that means, don’t worry, we’ll see it in action throughout this article.

Those are the upsides, but the big, obvious downside is that is makes things a little more complicated if you don’t understand how it all works (in reality, though, it’s simpler than the alternatives as the project gets larger)

C++ programs are built in a two stage process. First, each source file is compiled on its own. The compiler generates intermediate files for each compiled source file. These intermediate files are often called object files — but they are not to be confused with objects in your code. Once all the files have been individually compiled, it then links all the object files together, which generates the final binary (the program).

This means that each source file is compiled separately from other source files. As a result of this, in terms of compiling, «a.cpp» is clueless as to what’s going on inside of «b.cpp». Here’s a quick example to illustrate:

// in myclass.cpp class MyClass < public: void foo(); int bar; >; void MyClass::foo() < // do stuff >
// in main.cpp int main() < MyClass a; // Compiler error: 'MyClass' is unidentified return 0; >

Even though MyClass is declared in your program, it is not declared in main.cpp, and therefore when you compile main.cpp you get that error.

Читайте также:  Что такое автопрефиксы css

This is where header files come in. Header files allow you to make the interface (in this case, the class MyClass) visible to other .cpp files, while keeping the implementation (in this case, MyClass’s member function bodies) in its own .cpp file. That same example again, but tweaked slightly:

// in myclass.h class MyClass < public: void foo(); int bar; >;
// in myclass.cpp void MyClass::foo()
//in main.cpp // defines MyClass int main() < MyClass a; // no longer produces an error, because MyClass is defined return 0; >

The #include statement is basically like a copy/paste operation. The compiler will «replace» the #include line with the actual contents of the file you’re including when it compiles the file.

***************************************************************
** 2) What is the difference between .h/.cpp/.hpp/.cc/etc **
***************************************************************

All files are fundamentally the same in that they’re all text files, however different kinds of files should have different extensions:

Header files should use a .h__ extension (.h / .hpp / .hxx). Which of those you use doesn’t matter.
C++ Source files should use a .c__ extention (.cpp / .cxx / .cc). Which of those you use doesn’t matter.
C Source files should use .c (.c only).

The reason C and C++ source files are seperated here is because it makes a difference in some compilers. In all likelyhood (since you’re reading this on a C++ site), you’ll be using C++ code, so refrain from using the .c extension. Also, files with header extensions might be ignored by the compiler if you try to compile them.

So what’s the difference between Header files and Source files? Basically, header files are #included and not compiled, whereas source files are compiled and not #included. You can try to side-step these conventions and make a file with a source extension behave like a header or vice-versa, but you shouldn’t. I won’t list the many reasons why you shouldn’t (other than the few I already have) — just don’t.

The one exception is that it is sometimes (although very rarely) useful to include a source file. This scenario has to do with instantiating templates and is outside the scope of this article. For now. just put it in your brain: «do not #include source files» *****************************************************
** 3) Include guards **
*****************************************************

C++ compilers do not have brains of their own, and so they will do exactly what you tell them to. If you tell them to include the same file more than once, then that is exactly what they will do. And if you don’t handle it properly, you’ll get some crazy errors as a result:

// myclass.h class MyClass < void DoSomething() < >>;
// main.cpp // define MyClass // Compiler error - MyClass already defined 

Now you might be saying to yourself «well that’s stupid, why would I include the same file twice?» Believe it or not, it will happen a lot. Not quite as illustrated above, though. Usually it happens when you include two files that each include the same file. Example:

Читайте также:  Camel casing in java

Источник

Header files (C++)

The names of program elements such as variables, functions, classes, and so on must be declared before they can be used. For example, you can’t just write x = 42 without first declaring ‘x’.

int x; // declaration x = 42; // use x 

The declaration tells the compiler whether the element is an int , a double , a function, a class or some other thing. Furthermore, each name must be declared (directly or indirectly) in every .cpp file in which it is used. When you compile a program, each .cpp file is compiled independently into a compilation unit. The compiler has no knowledge of what names are declared in other compilation units. That means that if you define a class or function or global variable, you must provide a declaration of that thing in each additional .cpp file that uses it. Each declaration of that thing must be exactly identical in all files. A slight inconsistency will cause errors, or unintended behavior, when the linker attempts to merge all the compilation units into a single program.

To minimize the potential for errors, C++ has adopted the convention of using header files to contain declarations. You make the declarations in a header file, then use the #include directive in every .cpp file or other header file that requires that declaration. The #include directive inserts a copy of the header file directly into the .cpp file prior to compilation.

In Visual Studio 2019, the C++20 modules feature is introduced as an improvement and eventual replacement for header files. For more information, see Overview of modules in C++.

Example

The following example shows a common way to declare a class and then use it in a different source file. We’ll start with the header file, my_class.h . It contains a class definition, but note that the definition is incomplete; the member function do_something is not defined:

Next, create an implementation file (typically with a .cpp or similar extension). We’ll call the file my_class.cpp and provide a definition for the member declaration. We add an #include directive for «my_class.h» file in order to have the my_class declaration inserted at this point in the .cpp file, and we include to pull in the declaration for std::cout . Note that quotes are used for header files in the same directory as the source file, and angle brackets are used for standard library headers. Also, many standard library headers do not have .h or any other file extension.

In the implementation file, we can optionally use a using statement to avoid having to qualify every mention of «my_class» or «cout» with «N::» or «std::». Don’t put using statements in your header files!

// my_class.cpp #include "my_class.h" // header in local directory #include // header in standard library using namespace N; using namespace std; void my_class::do_something()

Now we can use my_class in another .cpp file. We #include the header file so that the compiler pulls in the declaration. All the compiler needs to know is that my_class is a class that has a public member function called do_something() .

// my_program.cpp #include "my_class.h" using namespace N; int main()

After the compiler finishes compiling each .cpp file into .obj files, it passes the .obj files to the linker. When the linker merges the object files it finds exactly one definition for my_class; it is in the .obj file produced for my_class.cpp, and the build succeeds.

Читайте также:  Тег INPUT

Include guards

Typically, header files have an include guard or a #pragma once directive to ensure that they are not inserted multiple times into a single .cpp file.

// my_class.h #ifndef MY_CLASS_H // include guard #define MY_CLASS_H namespace N < class my_class < public: void do_something(); >; > #endif /* MY_CLASS_H */ 

What to put in a header file

Because a header file might potentially be included by multiple files, it cannot contain definitions that might produce multiple definitions of the same name. The following are not allowed, or are considered very bad practice:

  • built-in type definitions at namespace or global scope
  • non-inline function definitions
  • non-const variable definitions
  • aggregate definitions
  • unnamed namespaces
  • using directives

Use of the using directive will not necessarily cause an error, but can potentially cause a problem because it brings the namespace into scope in every .cpp file that directly or indirectly includes that header.

Sample header file

The following example shows the various kinds of declarations and definitions that are allowed in a header file:

// sample.h #pragma once #include // #include directive #include namespace N // namespace declaration < inline namespace P < //. >enum class colors : short < red, blue, purple, azure >; const double PI = 3.14; // const and constexpr definitions constexpr int MeaningOfLife< 42 >; constexpr int get_meaning() < static_assert(MeaningOfLife == 42, "unexpected!"); // static_assert return MeaningOfLife; >using vstr = std::vector; // type alias extern double d; // extern variable #define LOG // macro definition #ifdef LOG // conditional compilation directive void print_to_log(); #endif class my_class // regular class definition, < // but no non-inline function definitions friend class other_class; public: void do_something(); // definition in my_class.cpp inline void put_value(int i) < vals.push_back(i); >// inline OK private: vstr vals; int i; >; struct RGB < short r< 0 >; // member initialization short g< 0 >; short b< 0 >; >; template // template definition class value_store < public: value_store() = default; void write_value(T val) < //. function definition OK in template >private: std::vector vals; >; template // template declaration class value_widget; > 

Feedback

Submit and view feedback for

Источник

Оцените статью