简体   繁体   中英

Why do you need inclusion guard for C++ header files?

I get roughly what it does. What I don't understand is why it's not the default? What are the use cases where some header file would need to be included multiple times?

The reason it's not the default is primarily historical these days -- when the C language was formalized, #include was specified that it must act exactly as if the user had copy-and-pasted the specified file's contents at the location of the #include -line; and C++ wanted (and wants) to remain as compatible as possible with C, so C++ inherited that behavior from C.

As for a use-case where including the same header file more than once might be useful; one instance where I found it useful was for simulating a templated-container-class in C (because C doesn't support templates directly). I had a container-implementation-header-file that looked something like this (but more elaborate; I'm showing a simplified version here for readability):

// MyContainerImplemention.h
// be sure to #define MYTYPE and MYARRAYSIZE
// before #include-ing this file!

struct ArrayOf##MYTYPE
{
   MYTYPE arrayOfItems[MYARRAYSIZE];
};

inline void Set##MYTYPE##Item(struct ArrayOf##MyType * container, int which, MYTYPE item) 
{
   container[which] = item;
}

[... and so on for various other MYTYPE-specific methods ...]

... then my .c files could do something like:

#define MYTYPE int
#define MYARRAYSIZE 10
#include "MyContainerImplementation.h"
#undef MYARRAYSIZE
#undef MYTYPE

#define MYTYPE short
#define MYARRAYSIZE 15
#include "MyContainerImplementation.h"
#undef MYARRAYSIZE
#undef MYTYPE

struct ArrayOfint myInts;
struct ArrayOfshort myShorts;

SetintItem(&myInts, 5, 12);
SetshortItem(&myShorts, 3, 2);
[...]

... and end up with the container "class" and its associated methods implemented for each data-type, without having to manually write a new implementation of the container "class" each time.

Yes, it was extremely ugly -- but not as ugly as having to manually write out thousands of lines of redundant container-code would have been. (The real container-implementation-header-file implemented a hash table and was several hundred lines long)

Without include guards or #pragma once the compiler would have to maintain a list of included files. This is not easy, because of different possible paths to these files (and #pragma once doesn't completely solve this) and would be expecting a bit much of the original C compilers, which had to work with very limited memory.

What's true today is not necessarily true when C came about and the C pre-processor, upon which the C++ one is based, was created.

#pragma once is just a step towards having proper C++ modules so this annoying historical legacy is finally eliminated.

Yes, it's valid to include a file multiple times, and yes, each time it's included it can behave in entirely different ways. This is why making pre-compiled headers is a huge headache for compiler developers.

Guard blocks or #pragma once are included in order to prevent a file from being included multiple times.

#pragma once , while supported on most compilers, is not an official part of the c++ standard, and may not work on every compiler. You can use a guard block, which will work on any compiler. An example of a guard block in the file MyClass.hpp would be:

#ifndef MYCLASS_HPP
#define MYCLASS_HPP

//Code here

#endif

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM