简体   繁体   中英

Where to put my standard library headers when creating classes in C++?

Correct me if I am wrong, the #ifndef stuff in the beginning of C++ headers specifically help avoid code from being repeated?

So (according to best practice) where do I put basic library headers like < iostream > or < string > ?

Let me clarify the question with an example

Lets say I have a program that creates Employee classes and stores info about them..like their names or something

  1. I have an int main() that uses std::cout ALOT to display info to console. It may even use hard coded string variables to store info into one of the classes (i know hard coding is bad just bear with me it will help clarify things for me)

  2. I have a header called Employee.h with all my private variables..in this case the name of the employee.. and it also had the function declarations. Lets pretend it uses a string data type in one of the constructors.

  3. an Employee.cpp that does setting and getting...cool Lets also pretend it has some weird function that manipulates the Employee name (which is a string) and add like "yolo" in front of it..cool. So this file will also require acsess to the < string > library

So far: all three of my files want to use the < string > header and only the main.cpp wants to use the < iostream > header.

My question(s):

Seems like the < string > header can be placed in the Eployee header and not the main.cpp and the program will compile and run just fine...but if i put it in the main and not the Employee header the compiler goes wild. So should I include it in both main and header or just the header?

Does this best practice change if I make a second class called Companies that also uses the string library?

PS can somone please explain how common preprocessors link stuff..like why does the computer throw erros when < string > is only included in main but not the header...I thought this stuff was all linked together...Im dumb pls explain

(I think i understand all the mumbo jumbo about obj files but then how doe preprocessors and compilers know that the header code isnt being repeated in both the class .cpp and the main .cpp?

Thank you for patience if you read through that, i also appreciate any help thrown this way towards a c++ noob like me.

According to the C++ standard:

A translation unit is the basic unit of compilation in C++. It consists of the contents of a single source file, plus the contents of any header files directly or indirectly included by it, minus those lines that were ignored using conditional preprocessing statements.

Note, the "indirectly included" wording. Any header to #include in another header is included in any source file that #include s that header. So if you require it to be included in the header, that is if your header requires a definition not simply a declaration , #include it there.

A small of example of how a translation unit is generated: Initially we have our header. Let's call it stdfoo.h (this is an example, you should avoid naming your own headers with std to avoid collision.)

#ifndef STD_FOO_ // here is our header guard
#define STD_FOO_

typedef long howl_t;
void foo();

#endif // STD_FOO_ close the header guard at the end

Let's include this in our project source main.cxx :

#include <stdfoo.h>

int main {
    foo();
    return 0;
};

When we compile this, it gets run through the preprocessor into a translation unit. Let's take a look at what that generated unit may look like:

typedef long howl_t;
void foo();

int main {
    foo();
    return 0;
};

The #include directive has expanded stdfoo.h in the translation unit so the compiler can look at the single translation unit and generate an object.

Let's change things up and give main.cxx a header, main.h .

#ifndef MAIN_H
#define MAIN_H

class BarkBark {
    BarkBark() {}
    void emit();
};

#endif // MAIN_H

Use it in our new main.cxx :

#include "main.h"
#include <stdfoo.h>

int main {
    BarkBark woof;
    woof.emit();
    foo();
    return 0;
};

The translation unit would look like:

class BarkBark {
    BarkBark() {}
    void emit();
};

typedef long howl_t;
void foo();

int main {
    BarkBark woof;
    woof.emit();
    foo();
    return 0;
};

Now say emit uses howl_t as an argument like so void emit(howl_t h) this would require either redeclaring howl_t, a dicey way to go, or including stdfoo.h in main.h

#ifndef MAIN_H
#define MAIN_H

#include <stdfoo.h>

class BarkBark {
    BarkBark() {}
    void emit(howl_t h);
};

#endif // MAIN_H

How does that translation unit look?

typedef long howl_t;
void foo();

class BarkBark {
    BarkBark() {}
    void emit();
};

int main {
    howl_t aroooooo = 0;
    BarkBark woof;
    woof.emit(aroooooo);
    return 0;
};

The preprocessor has expanded the #include inside of the #include d header.

Note whether or not #include <stdfoo.h> remains in main.cxx the translation unit would look the same due to the preprocessor handling the header guards, the second inclusion is simply discarded.

Now, as far as standard library headers, most of not all standard libraries guard against multiple inclusion, so you can #include then as frequently and often as your heart desires with no ill effect. The resulting translation unit will only include it once for compilation. Do note this is not guaranteed with any other headers and including unguarded headers multiple times can yield very obscure looking errors as the duplicate declarations only exist in the translation unit.

So to answer your question, if your header requires a particular class or function to be included, #include it in the header. At that point, it is a stylistic choice whether you decide to #include it in the source file as well. Some say do in case the header changes, some say do not to simplify code. That choice is really up to you.

If a header is not required in another header file, you are much better off only including it in each source file that requires it so that your code produces smaller translation units for compilation and in order to reduce namespace pollution.

However! This is not a hard and fast rule. Some do it differently and arrange #include s into logical positions where they may be used or where certain headers are used in multiple source files to reduce code duplication. Personally, I think of this arrangement added technical debt if code may be improved or refactored down the line and headers become orphaned.

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