While you can include
.cpp files as you mentioned, this is a bad idea.
As you mentioned, declarations belong in header files. These cause no problems when included in multiple compilation units because they do not include implementations. Including a the definition of a function or class member multiple times will normally cause a problem (but not always) because the linker will get confused and throw an error.
What should happen is each
.cpp file includes definitions for a subset of the program, such as a class, logically organized group of functions, global static variables (use sparingly if at all), etc.
Each compilation unit (
.cpp file) then includes whatever declarations it needs to compile the definitions it contains. It keeps track of the functions and classes it references but does not contain, so the linker can resolve them later when it combines the object code into an executable or library.
Foo.h -> contains declaration (interface) for class Foo.
Foo.cpp -> contains definition (implementation) for class Foo.
Main.cpp -> contains main method, program entry point. This code instantiates a Foo and uses it.
Main.cpp need to include
Foo.cpp needs it because it is defining the code that backs the class interface, so it needs to know what that interface is.
Main.cpp needs it because it is creating a Foo and invoking its behavior, so it has to know what that behavior is, the size of a Foo in memory and how to find its functions, etc. but it does not need the actual implementation just yet.
The compiler will generate
Foo.cpp which contains all of the Foo class code in compiled form. It also generates
Main.o which includes the main method and unresolved references to class Foo.
Now comes the linker, which combines the two object files
Main.o into an executable file. It sees the unresolved Foo references in
Main.o but sees that
Foo.o contains the necessary symbols, so it "connects the dots" so to speak. A function call in
Main.o is now connected to the actual location of the compiled code so at runtime, the program can jump to the correct location.
If you had included the
Foo.cpp file in
Main.cpp, there would be two definitions of class Foo. The linker would see this and say "I don't know which one to pick, so this is an error." The compiling step would succeed, but linking would not. (Unless you just do not compile
Foo.cpp but then why is it in a separate
Finally, the idea of different file types is irrelevant to a C/C++ compiler. It compiles "text files" which hopefully contain valid code for the desired language. Sometimes it may be able to tell the language based on the file extension. For example, compile a
.c file with no compiler options and it will assume C, while a
.cpp extension would tell it to assume C++. However, I can easily tell a compiler to compile a
.h or even
.docx file as C++, and it will emit an object (
.o) file if it contains valid C++ code in plain text format. These extensions are more for the benefit of the programmer. If I see
Foo.cpp, I immediately assume that the first contains the declaration of the class and the second contains the definition.