38

When we design classes in Java, Vala, or C# we put the definition and declaration in the same source file. But in C++ it is traditionally preferred to separate the definition and declaration in two or more files.

What happens if I just use a header file and put everything into it, like Java? Is there a performance penalty or something?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
sepisoad
  • 2,201
  • 5
  • 26
  • 37
  • 5
    Then you defeat the entire purpose of separating interface from implementation. – Ed S. Feb 10 '11 at 09:15
  • 2
    Not only that, you will end up compiling your whole application as one big source file, and every little change will cause a recompilation of your complete application. – Patrick Feb 10 '11 at 09:18
  • 4
    Also see: [What are the advantages and disadvantages of separating declaration and definition as in C++?](http://stackoverflow.com/questions/645778/what-are-the-advantages-and-disadvantages-of-separating-declaration-and-definitio) – Cody Gray - on strike Feb 10 '11 at 09:20
  • 2
    Terminology. In C++, this is a class *declaration*: `class Foo;`. This is a class *definition*: `class Foo { void foo(); int bar(const char*);};`. In C++, one usually puts class *definitions* in header files, and the decision to make is where to put the definitions of the member functions. – Steve Jessop Feb 10 '11 at 10:02
  • 2
    @Ed S.: no you don't. The standard template containers and algorithms have complete definitions in header files, and yet they manage to logically separate interface from implementation fine. The interface is in the standard, not in the header files. Perhaps from C, one way in which people sometimes separate interface from implementation in C++ is to use function signatures etc., in a header file, as an interface definition. However, that is not the only way to define an interface in order to separate it from implementation. I doubt it's the most common way in C++ (well, I hope it isn't). – Steve Jessop Feb 10 '11 at 10:08
  • What you no longer do, if everything is in headers, is separate the two from the POV of the build process, so it's no longer possible for a TU to quickly get just what it needs to compile its code and link dependencies later. That is not the "entire purpose" of separating interface from implementation, though, it's a useful optimization that happens to bear some similarity to an important design principle. – Steve Jessop Feb 10 '11 at 10:11
  • Well yes, it's true that templates are often implemented in the header files. Separation of interface from implementation isn't the *only* reason, but it is a big one, and it's not a good idea to stuff everything in a header file if you can avoid it. – Ed S. Feb 10 '11 at 17:54

5 Answers5

74

The answer depends on what kind of class you're creating.

C++'s compilation model dates back to the days of C, and so its method of importing data from one source file into another is comparatively primitive. The #include directive literally copies the contents of the file you're including into the source file, then treats the result as though it was the file you had written all along. You need to be careful about this because of a C++ policy called the one definition rule (ODR) which states, unsurprisingly, that every function and class should have at most one definition. This means that if you declare a class somewhere, all of that class's member functions should be either not defined at all or defined exactly once in exactly one file. There are some exceptions (I'll get to them in a minute), but for now just treat this rule as if it's a hard-and-fast, no-exceptions rule.

If you take a non-template class and put both the class definition and the implementation into a header file, you might run into trouble with the one definition rule. In particular, suppose that I have two different .cpp files that I compile, both of which #include your header containing both the implementation and the interface. In this case, if I try linking those two files together, the linker will find that each one contains a copy of the implementation code for the class's member functions. At this point, the linker will report an error because you have violated the one definition rule: there are two different implementations of all the class's member functions.

To prevent this, C++ programmers typically split classes up into a header file which contains the class declaration, along with the declarations of its member functions, without the implementations of those functions. The implementations are then put into a separate .cpp file which can be compiled and linked separately. This allows your code to avoid running into trouble with the ODR. Here's how. First, whenever you #include the class header file into multiple different .cpp files, each of them just gets a copy of the declarations of the member functions, not their definitions, and so none of your class's clients will end up with the definitions. This means that any number of clients can #include your header file without running into trouble at link-time. Since your own .cpp file with the implementation is the sole file that contains the implementations of the member functions, at link time you can merge it with any number of other client object files without a hassle. This is the main reason that you split the .h and .cpp files apart.

Of course, the ODR has a few exceptions. The first of these comes up with template functions and classes. The ODR explicitly states that you can have multiple different definitions for the same template class or function, provided that they're all equivalent. This is primarily to make it easier to compile templates - each C++ file can instantiate the same template without colliding with any other files. For this reason, and a few other technical reasons, class templates tend to just have a .h file without a matching .cpp file. Any number of clients can #include the file without trouble.

The other major exception to the ODR involves inline functions. The spec specifically states that the ODR does not apply to inline functions, so if you have a header file with an implementation of a class member function that's marked inline, that's perfectly fine. Any number of files can #include this file without breaking the ODR. Interestingly, any member function that's declared and defined in the body of a class is implicitly inline, so if you have a header like this:

#ifndef Include_Guard
#define Include_Guard

class MyClass {
public:
    void DoSomething() {
        /* ... code goes here ... */
    }
};

#endif

Then you're not risking breaking the ODR. If you rewrite this as

#ifndef Include_Guard
#define Include_Guard

class MyClass {
public:
    void DoSomething();
};

void MyClass::DoSomething()  {
    /* ... code goes here ... */
}

#endif

then you would be breaking the ODR, since the member function isn't marked inline and if multiple clients #include this file there will be multiple definitions of MyClass::DoSomething.

So to summarize - you should probably split up your classes into a .h/.cpp pair to avoid breaking the ODR. However, if you're writing a class template, you don't need the .cpp file (and probably shouldn't have one at all), and if you're okay marking every single member function of your class inline you can also avoid the .cpp file.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • As of today, would it be a good practice to put declaration and definition in the same file? At least it helps when I update the class. I don't have to swap between my header file and the cpp file. – Oh Xyz Jul 03 '20 at 00:17
  • Then what is the purpose of include guards? Don't they prevent the breaking of ODR? – Abdel Aleem Aug 22 '21 at 11:31
  • 1
    @AbdelAleem It’s okay for multiple translation units to have identical definitions of the same class or struct, or identical definitions of the same template or inline function. What’s not okay is for one translation unit to have multiple definitions of the same class or struct. That’s the issue include guards address - it ensures the same translation unit doesn’t get multiple copies of a single class definition. – templatetypedef Aug 22 '21 at 13:39
7

The drawback of putting definition in header files is as follows:-

Header file A - contains definition of metahodA()

Header file B - includes header file A.

Now let us say you change the definition of methodA. You would need to compile file A as well as B because of the inclusion of header file A in B.

sachin
  • 1,141
  • 2
  • 16
  • 21
1

The biggest difference is that every function is declared as an inline function. Generally your compiler will be smart enough that this won't be a problem, but worst case scenario it will cause page faults on a regular basis and make your code embarrassingly slow. Usually the code is separated for design reasons, and not for performance.

regality
  • 6,496
  • 6
  • 29
  • 26
0

In general, it is a good practice to seperate implementation from headers. However, there are exceptions in cases like templates where the implementation goes in the header itself.

Mahesh
  • 34,573
  • 20
  • 89
  • 115
0

Two particular problems with putting everything in the header:

  1. Compile times will be increased, sometimes greatly. C++ compile times are long enough that that's not something you want.

  2. If you have circular dependencies in the implementation, keeping everything in headers is difficult to impossible. eg:

    header1.h

    struct C1
    {
      void f();
      void g();
    };
    

    header2.h

    struct C2
    {
      void f();
      void g();
    };
    

    impl1.cpp

    #include "header1.h"
    #include "header2.h"
    
    void C1::f()
    {
      C2 c2;
      c2.f();
    }
    

    impl2.cpp

    #include "header2.h"
    #include "header1.h"
    
    void C2::g()
    {
      C1 c1;
      c1.g();
    }
    
ymett
  • 2,425
  • 14
  • 22