3

Instead of doing

#include "MyClass.cpp"

I would like to do

#include "MyClass.h"

I've read online that not doing so is considered bad practice.

sbi
  • 219,715
  • 46
  • 258
  • 445
Davide Valdo
  • 779
  • 8
  • 21

7 Answers7

12

Separate compilation in a nutshell

First, let's get some quick examples out there:

struct ClassDeclaration;   // 'class' / 'struct' mean almost the same thing here
struct ClassDefinition {}; // the only difference is default accessibility
                           // of bases and members

void function_declaration();
void function_definition() {}

extern int global_object_declaration;
int global_object_definition;

template<class T>           // cannot replace this 'class' with 'struct'
struct ClassTemplateDeclaration;
template<class T>
struct ClassTemplateDefinition {};

template<class T>
void function_template_declaration();
template<class T>
void function_template_definition() {}

Translation Unit

A translation unit (TU) is a single source file (should be a **.cpp* file) and all the files it includes, and they include, etc. In other words: the result of preprocessing a single file.

Headers

Include guards are a hack to work around lack of a real module system, making headers into a kind of limited module; to this end, including the same header more than once must not have an adverse affect.

Include guards work by making subsequent #includes no-ops, with the definitions available from the first include. Because of their limited nature, macros which control header options should be consistent throughout a project (oddball headers like <assert.h> cause problems) and all #includes of public headers should be outside of any namespace, class, etc., usually at the top of any file.

See my include guard naming advice, including a short program to generate include guards.

Declarations

Classes, functions, objects, and templates may be declared almost anywhere, may be declared any number of times, and must be declared before referring to them in any way. In a few weird cases, you can declare classes as you use them; won't cover that here.

Definitions

Classes may be defined at most once[1] per TU; this typically happens when you include a header for a particular class. Functions and objects must be defined once in exactly one TU; this typically happens when you implement them in a **.cpp* file. However, inline functions, including implicitly inline functions inside class definitions, may be defined in multiple TUs, but the definitions must be identical.

For practical purposes[2], templates (both class templates and function templates) are defined only in headers, and if you want to use a separate file, then use another header[3].

[1] Because of the at-most-once restriction, headers use include guards to prevent multiple inclusion and thus multiple definition errors.
[2] I won't cover the other possibilities here.
[3] Name it blahblah_detail.hpp, blahblah_private.hpp, or similar if you want to document that it's non-public.

Guidelines

So, while I'm sure everything above is all a big ball of mud so far, it's less than a page on what should take up a few chapters, so use it as a brief reference. Understanding the concepts above, however, is important. Using those, here's a short list of guidelines (but not absolute rules):

  • Always name headers consistently in a single project, such as **.h* for C and **.hpp* for C++.
  • Never include a file which is not a header.
  • Always name implementation files (which are going to be directly compiled) consistently, such as **.c* and **.cpp*.
  • Use a build system which can compile your source files automatically. make is the canonical example, but there are many alternatives. Keep it simple in simple cases. For example, make can be used its built-in rules and even without a makefile.
  • Use a build system which can generate header dependencies. Some compilers can generate this with command-line switches, such as -M, so you can make a surprisingly useful system easily.

Build Process

(Here's the tiny bit that answers your question, but you need most of the above in order to get here.)

When you build, the build system will then go through several steps, of which the important ones for this discussion are:

  1. compile each implementation file as a TU, producing an object file (**.o*, **.obj*)
    • each is compiled independently of the others, which is why each TU needs declarations and definitions
  2. link those files, along with libraries specified, into a single executable

I recommend you learn the rudiments of make, as it is popular, well-understood, and easy to get started with. However, it's an old system with several problems, and you'll want to switch to something else at some point.

Choosing a build system is almost a religious experience, like choosing an editor, except you'll have to work with more people (everyone working on the same project) and will likely be much more constrained by precedent and convention. You can use an IDE which handles the same details for you, but this has no real benefit from using a comprehensive build system instead, and you really should still know what it's doing under the hood.

File Templates

example.hpp

#ifndef EXAMPLE_INCLUDE_GUARD_60497EBE580B4F5292059C8705848F75
#define EXAMPLE_INCLUDE_GUARD_60497EBE580B4F5292059C8705848F75
// all project-specific macros for this project are prefixed "EXAMPLE_"

#include <ostream> // required headers/"modules"/libraries from the
#include <string>  // stdlib, this project, and elsewhere
#include <vector>

namespace example { // main namespace for this project
template<class T>
struct TemplateExample { // for practical purposes, just put entire
  void f() {}            // definition of class and all methods in header
  T data;
};

struct FooBar {
  FooBar(); // declared
  int size() const { return v.size(); } // defined (& implicitly inline)
private:
  std::vector<TemplateExample<int> > v;
};

int main(std::vector<std::string> args); // declared
} // example::

#endif

example.cpp

#include "example.hpp" // include the headers "specific to" this implementation
// file first, helps make sure the header includes anything it needs (is
// independent)

#include <algorithm> // anything additional not included by the header
#include <iostream>

namespace example {
FooBar::FooBar() : v(42) {} // define ctor

int main(std::vector<std::string> args) { // define function
  using namespace std; // use inside function scope, if desired, is always okay
  // but using outside function scope can be problematic
  cout << "doing real work now...\n"; // no std:: needed here
  return 42;
}
} // example::

main.cpp

#include <iostream>
#include "example.hpp"

int main(int argc, char const** argv) try {
  // do any global initialization before real main
  return example::main(std::vector<std::string>(argv, argv + argc));
}
catch (std::exception& e) {
  std::cerr << "[uncaught exception: " << e.what() << "]\n";
  return 1; // or EXIT_FAILURE, etc.
}
catch (...) {
  std::cerr << "[unknown uncaught exception]\n";
  return 1; // or EXIT_FAILURE, etc.
}
Community
  • 1
  • 1
  • I'm going to have to come back and edit this for clarity after some time. Unfortunately I don't quite see how to make it shorter and still be useful for you. This is one topic much better covered in a different medium, like a book, I think. –  Jan 10 '10 at 20:00
  • 2
    Yeah, it is impossible to explain the C++ compilation model much shorted than you did it. Unfortunately, because it's pretty important information. +1 from me for a brave attempt ;) – jalf Jan 12 '10 at 05:04
  • If I may ask, how is it possible for outside function scoping to cause problems? – Davide Valdo Jan 17 '10 at 08:55
  • @Dave: what do you mean by outside function scoping? –  Jan 17 '10 at 14:05
  • Sorry, I meant "scope". I am talking about the problems that the "using" directive might cause if used outside the scope of a single function. – Davide Valdo Jan 17 '10 at 14:18
  • Ah, using-directives ("using namespace example;") affect everything that comes later in the TU, "import" a ton of names (more than expected by the user, plus names that weren't intended to be "public" by the author), and should especially be avoided in headers---but it is possible to use them correctly elsewhere. However, I always advise to use them only in function scope (the "anyone who knows how to do it correctly doesn't have to ask" conundrum); then they are limited to exactly that scope and can't cause confusion for a new user. –  Jan 17 '10 at 14:34
  • Remember you don't have to use a using directive, but it is a common question. You want to limit interaction between logically separate units (which often translates into namespaces), and you don't need a directive for names in the "current" namespace. If it's just one or two names, I find it's often easier to prefix them. –  Jan 17 '10 at 14:38
3

This is called separate compilation model. You include class declarations into each module where they are needed, but define them only once.

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • You define classes in headers, so they get defined in *every* TU. (And Wikipedia is somewhere between misleading and wrong about parts of the ODR, too.) It's *objects* and (non-inline) *functions* that are declared in headers and defined exactly once. –  Jan 10 '10 at 18:03
  • What is exactly the difference between "definition" and "declaration"? – Davide Valdo Jan 10 '10 at 18:32
  • A declaration only introduces a 'signature', but the definition really refers to function or method bodies. Its true that a class can be both declared and defined in one statement, but you would normally put this in a header file. A class can also be declared in a header, leaving its method bodies to be defined in a .cpp file. – quamrana Jan 10 '10 at 18:57
  • 1
    Examples: `class Declaration; class Definition {}; void function_declaration(); void function_definition() {} extern int global_obj_declaration; int global_obj_definition;` –  Jan 10 '10 at 19:02
2

In addition to hiding implementation details in cpp files (check other replies), you can additionally hide structure details by class forward declaration.

class FooPrivate;  

class Foo  
{  
  public:  
  // public stuff goes here  
  private:  
  FooPrivate *foo_private;  
};

The expression class FooPrivate says that FooPrivate is completely defined somewhere else (preferably in the same file where Foo's implementation resides, before Foo's stuff comes. This way you make sure that implementation details of Foo(Private) aren't exposed via the header file.

avakar
  • 32,009
  • 9
  • 68
  • 103
Damg
  • 406
  • 3
  • 9
  • It's done quite a lot - I think he's talking about the PIMPL idiom. –  Jan 10 '10 at 18:53
  • My bad, I didn't read the whole answer. I thought Damg was talking about opaque types aka `FILE`. – avakar Jan 10 '10 at 18:55
  • Dave: C++ is *terribly* complex and *convoluted*, but has mostly maintained backwards compatibility with C, itself, and all kinds of tools; it's a tradeoff. –  Jan 10 '10 at 20:34
  • To be honest I think the problem is more the design than the idea itself, there are a lot of things which might have been implemented a lot better. The syntax is inconsistent and noisy, the semantics are ambiguous and one has to come up with weird hacks (like this "include guard" thing) to follow software quality criteria. Moreover, the standard library contains plenty of tools to move around language defects (what the hell is std::auto_ptr?) but lacks important features like networking and multithreading. – Davide Valdo Jan 10 '10 at 21:17
  • Dave, C++ has it's downsides (mostly inherited from C guards), but I urge you to reserve judgment for after you get better acquainted with it. There are lots of features that make the language worth embracing. – avakar Jan 10 '10 at 21:22
  • Dave: It sounds like you're approaching C++ from a different language and becoming disappointed that it doesn't work exactly as that language---this is common to do, even by accident, when learning new programming languages, and this approach never leaves you satisfied, no matter which two languages you plug into it. Additionally, there are benefits to all of the problems you've mentioned, even if you can't see them now, but any good programmer will tell you to use the right tool for the job, and that isn't always C++. –  Jan 10 '10 at 22:01
2

You needn't include .c or .cpp files - the compiler will compile them regardless whether they're #included in other files or not. However, the code in the .c/.cpp files is useless if the other files are unaware of the classes/methods/functions/global vars/whatever that's contained in them. And that's where headers come into play. In the headers, you only put declarations, such as this one:

//myfile.hpp
class MyClass {
    public:
        MyClass (void);
        void myMethod (void);
        static int myStaticVar;
    private:
        int myPrivateVar;
};

Now, all .c/.cpp files that will #include "myfile.hpp" will be able to create instances of MyClass, operate on myStaticVar and call MyClass::myMethod(), even though there's no actual implementation here! See?

The implementation (the actual code) goes into myfile.cpp, where you tell the compiler what all your stuff does:

//myfile.cpp
int MyClass::myStaticVar = 0;

MyClass::MyClass (void) {
    myPrivateVar = 0;
}

void MyClass::myMethod (void) {
    myPrivateVar++;
}

You never include this file anywhere, it's absolutely not necessary.

A tip: create a main.hpp (or main.h, if you prefer - makes no difference) file and put all the #includes there. Each .c/.cpp file will then only need to have have this line: #include "main.hpp". This is enough to have access to all classes, methods etc. you declared in your entire project :).

mingos
  • 23,778
  • 12
  • 70
  • 107
  • Of course, main.hpp with all the #includes has one drawback: a change in a single header file will make the compiler recompile the entire program instead of only the source files that actually use the header :D. – mingos Jan 10 '10 at 19:23
  • And the other drawback is that you lose control over dependencies - by adding `#include "main.hpp"` you effectively say that everything can depend on everything else. That's bad if you have some useful functionality that another project might be able to make use of. – quamrana Jan 11 '10 at 18:45
  • Yep, that's true too. But it's still a great way of doing things with as little effort as possible :). – mingos Jan 11 '10 at 22:39
1

You should not include a source file (.c or .cpp). Instead you should include the corresponding header file(.h) containing the declarations. The source files needs to be compiled separately and linked together to get the final executable.

gameover
  • 11,813
  • 16
  • 59
  • 70
1

Cpp files should be defined in your compiler script to be compiled as object files.

What ide are you using? I am going to assume you are compiling with gcc, so here is the command to compile two .cpp files into one executable

gcc -o myclasses.out myclass.cpp myotherclass.cpp

You should only use #include to include class definitions, not the implentation

Charles
  • 3,734
  • 3
  • 31
  • 49
1

One thing you will want to watch out for when including you class declarations from a .h/.hpp is make sure it only ever gets included once. If you don't do this you will get some possibly cryptic compiler errors that will drive you up the wall.

To do this you need to tell the compiler, using a #define, to include the file only if the #define does not already exist.

For example (MyClass.h):

#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass 
{
// Memebers and methods
}
#endif
// End of file

This will guarantee your class declaration only gets included once even if you have it included in many different .cpp files.

MadcapLaugher
  • 573
  • 2
  • 8
  • That include guard macro is reserved, don't use it. http://stackoverflow.com/questions/1744144/adding-ifndef-define-endif-breaks-the-compile/1744302#1744302 –  Jan 10 '10 at 19:21
  • The "INCLUDE" wasn't the reason it was a reserved name. –  Jan 10 '10 at 19:44
  • 1
    @MadcapLaughter: try changing the include guard macro name to: `MYCLASS_H`, or `MYCLASS_INCLUDE` if you like. – quamrana Jan 11 '10 at 18:47