It is a dependency issue.
When you #include
one file into another file, the entire content of the included file becomes part of the file that includes it. Remember that #include
is a preprocesssor directive. It is processed before the compiler is invoked, which parses the output of the preprocessor. Thus the compiler ends up seeing a single consolidated code with no knowledge that the content may have originated from different sources. The code is compiled as-is from start to end. For example:
Question.h
class question() {
private:
string ques;
public:
question(string ques){
this->ques = ques;
}
};
A.cpp
#include "Question.h"
void doSomething() {
question q("something");
...
}
B.cpp
#include "Question.h"
void doSomethingElse() {
question q("something else");
...
}
When A.cpp
and B.cpp
are compiled, the preprocessor merges the content of Question.h
into them, and the compiler sees this code:
A.cpp
class question() {
private:
string ques;
public:
question(string ques){
this->ques = ques;
}
};
void doSomething() {
question q("something");
...
}
B.cpp
class question() {
private:
string ques;
public:
question(string ques) {
this->ques = ques;
}
};
void doSomethingElse() {
question q("something else");
...
}
So, any changes you make to Question.h
affects the content of both A.cpp
and B.cpp
and thus they both have to be recompiled.
You want to minimize how many files have to be recompiled when you make a change to a given file. By separating the declaration and implementation into separate .h
and .cpp
files, and then you #include
only the .h
file in other files, changes you make to the .cpp
file do not affect those other files, so they do not have to be recompiled, speeding up compiling time. Only changes to the .h
file will cause them to be recompiled. For example:
Question.h
class question() {
private:
string ques;
public:
question(string ques);
};
Question.cpp
#include "Question.h"
question::question(string ques) {
this->ques = ques;
}
A.cpp
#include "Question.h"
void doSomething() {
question q("something");
...
}
B.cpp
#include "Question.h"
void doSomething() {
question q("something");
...
}
When compiled, the compiler sees:
Question.cpp
class question() {
private:
string ques;
public:
question(string ques);
};
question::question(string ques) {
this->ques = ques;
}
A.cpp
class question() {
private:
string ques;
public:
question(string ques);
};
void doSomething() {
question q("something");
...
}
B.cpp
class question() {
private:
string ques;
public:
question(string ques);
};
void doSomethingElse() {
question q("something else");
...
}
You can make any changes you want to Question.cpp
, and only Question.cpp
will be recompiled accordingly. A.cpp
and B.cpp
are not being changed and thus will not be recompiled, unless you change Question.h
, in which case they are.
Also, the separation lends itself to the use of precompiled headers, which also helps to speed up compiling time. A PCH consists of #include
files that do not change over time, so they can be compiled once and the output cached and reused wherever an #include
refers to the PCH. As long as the PCH itself or any of its dependent files are not changed, files that #include
the PCH are recompiled only when you change other things they are referring it.
There is another aspect to this separation to consider. If everything were declared and implemented in a single file, and then you #include
that file into multiple files, they each receive their own copy of any global variables that are declared. For example:
Question.h
class question() {
private:
string ques;
public:
question(string ques){
this->ques = ques;
}
};
int myGlobal; // <--
A.cpp
#include "Question.h"
void doSomething() {
question q("something");
...
}
B.cpp
#include "Question.h"
void doSomethingElse() {
question q("something else");
...
}
When compiled, the compiler sees this:
A.cpp
class question() {
private:
string ques;
public:
question(string ques){
this->ques = ques;
}
};
int myGlobal; // <--
void doSomething() {
question q("something");
...
}
B.cpp
class question() {
private:
string ques;
public:
question(string ques){
this->ques = ques;
}
};
int myGlobal; // <--
void doSomethingElse() {
question q("somethingElse");
...
}
Now A.cpp
and B.cpp
both have their own global variable that have the same name. This can cause a linker conflict. It may fail to link altogether. It it may decide to discard one and keep the other. It may decide to keep both. In the latter case, you end up with multiple copies of data, and that can cause subtle inconsistencies at runtime if a given portion of code is manipulating one variable when it is actually expecting to manipulate another variable of the same name. This can be very hard to debug if you are not careful.
By separating the .h
and .cpp
files, and declaring the global variable as extern
in the .h
file and defining its memory storage in the corresponding .cpp
file, there is only one variable at compile-time, and other files that #include
the .h
file will merely receive a reference to that single variable. For example:
Question.h
class question() {
private:
string ques;
public:
question(string ques);
};
extern int myGlobal; // <--
Question.cpp
#include "Question.h"
int myGlobal = 0; // <--
question::question(string ques) {
this->ques = ques;
}
A.cpp
#include "Question.h"
void doSomething() {
question q("something");
...
}
B.cpp
#include "Question.h"
void doSomethingElse() {
question q("something else");
...
}
When compiled, the compiler sees this:
Question.cpp
class question() {
private:
string ques;
public:
question(string ques);
};
extern int myGlobal; // <--
int myGlobal = 0; // <--
question::question(string ques) {
this->ques = ques;
}
A.cpp
class question() {
private:
string ques;
public:
question(string ques);
};
extern int myGlobal; // <--
void doSomething() {
question q("something");
...
}
B.cpp
class question() {
private:
string ques;
public:
question(string ques);
};
extern int myGlobal; // <--
void doSomethingElse() {
question q("something else");
...
}
The linker (not the compiler) is responsible for resolving those external references as needed so there is only one variable in the final executable that all relevant code is accessing.