11

In C# or Java, classes are declared and defined at the same time. In C++, the norm is to do that separately. What if we write the whole class in one , say .cpp, file and include that in files that references to it, what kinds of bad thing technically would happen besides a lengthened compilation process?

gilbertc
  • 1,049
  • 1
  • 10
  • 19
  • 1
    See this question http://stackoverflow.com/questions/1001639/coding-c-without-headers-best-practices and, in particular, my remarkable answer to it :) http://stackoverflow.com/questions/1001639/coding-c-without-headers-best-practices/1001749#1001749 – Daniel Daranas Mar 19 '10 at 18:13
  • 1
    +1 Most programmers obediently follow the rules without questioning them. You do ask questions, which is a good thing. – StackedCrooked Mar 19 '10 at 18:15
  • I think you meant "member functions" are declared and defined at the same time. The list of members IS the class definition, even if none of the members have bodies yet. (And yes, you can also forward declare classes, that's just class MyClass; with no members listed at all), it lets you start making pointers to objects of that class. – Ben Voigt Mar 19 '10 at 19:57
  • Even if the code will compile and run, the Maintainability Gremlins will find you and steal your socks while you sleep. – bta Mar 19 '10 at 20:25

8 Answers8

12

If your implementation of MyClass is all in the header file MyClass.h then any file you needed to implement MyClass will be included whenever someone includes MyClass.h.

If you change any part of MyClass.h, even if it's trivial (such as adding a comment or even a space) then all files that include it will have to recompile, even if the interface hasn't changed.

Neither of these matters for toy projects, but as you noted, when you have a program that consists of hundreds (or thousands, etc.) of class files, the added compilation time alone makes it worthwhile to separate out implementation from interface.

For instance, if I have the following:

// MyClass.h
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

#include "Inventory.h"

class MyClass
{
public:
  MyClass();

  void processInventory(Inventory& inventory)
  {
    // Do something with each item in the inventory here
    // that uses iostream, iomanip, sstream, and string
  }
private:
  // ...
};

It would more ideomatically be written as:

// MyClass.h
class Inventory;

class MyClass
{
public:
  MyClass();

  void processInventory(Inventory& inventory);
private:
  // ...
};

// MyClass.cc
#include "MyClass.h"

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

#include "Inventory.h"

MyClass()::MyClass()
{
}

void MyClass()::processInventory(Inventory& inventory)
{
  // Do something with each item in the inventory here
  // that uses iostream, iomanip, sstream, and string
}

Notice: Including MyClass.h doesn't mean iostream, iomanip, sstream, string, or Inventory.h have to be parsed. Changing how processInventory works doesn't mean all files using MyClass.h have to be recompiled.

Notice how much easier it can be to figure out how to use MyClass now. Header files serve an important purpose: they show people how to use your class. With the modified MyClass.h it's easy to see the list of functions. If each function is defined in the header, then you can't look at just the list of functions. That makes it harder to figure out how to use the class.

Bill
  • 14,257
  • 4
  • 43
  • 55
  • +1 -- Inlining everything also increases code size and breaks Intellisense in Visual Studio 2008. – Billy ONeal Mar 19 '10 at 18:27
  • 2
    @Billy: Good to know about the Intellisense, thanks. However, inlining may increase code size, or it may decrease code size. Inlining may increase execution time, or it may decrease execution time. These things are highly variable, which is why the inline keyword now does very little in encouraging a compiler to inline a function. – Bill Mar 19 '10 at 18:28
  • Yes. That is the 'lengthen compilation process' I mentioned and I agree that would be a potential problem (what if the project expands). But, if it is not a problem, would that still be feasible to define/declare class in one single file? – gilbertc Mar 19 '10 at 18:30
  • @gilbertc: I updated my response to talk about how inline functions make it harder for other people (and you in 6-months) to see how to use your class. – Bill Mar 19 '10 at 18:33
  • @Bill: thanks for you update. but still, with a modern decent IDE with code-folding and autocompletes, I don't think .h files with definitions only serve as much purpose as it did in the past. – gilbertc Mar 19 '10 at 18:39
  • @gilbertc: For C# I definitely agree with you. C# is a much easier language to parse. C++ is sufficiently complex to parse that you don't want to recompile hundreds of files because you changed something minor. There's also the question of delivery. Traditionally, you send your header file + object file to your customer. This allows you to change the implementation and send out a new object file, which your customer can re-link agaist without having to recompile. – Bill Mar 19 '10 at 18:51
  • It is still possible to have the entire class definition in the header while maintaining the desired "list of functions." Also, no rule is hard and fast. Boost has many header-only libraries, but some stuff is compiled, as an example. – tJener Mar 19 '10 at 19:44
  • 1
    @tJener: That's true, although many of Boost's libraries are header-only because they're heavily (or completely) template based. – Bill Mar 19 '10 at 21:04
5

You may break the one definition rule.

If you write this:

class foo
{
public:
    void doit();
};

foo::doit() {}

and include that in multiple classes, you will have multiple definitions of foo::doit and your link will fail.

But if you make all your classes inline, either by defining them within the class declaration:

class foo
{
public:
    void doit() {
    }
};

or by explicitly making them inline:

class foo
{
public:
    void doit();
};

inline void foo::doit() {}

then you can include that file as many times as you like.

R Samuel Klatchko
  • 74,869
  • 16
  • 134
  • 187
2

The linker will see multiple definitions of the class's members when you try to combine multiple such objects. Thus, you won't be able to produce a binary from source files that include anything in more than one place.

Phil Miller
  • 36,389
  • 13
  • 67
  • 90
1

Typically you separate the declaration and definition of a class. This allows you to use your class in different source files by simply including the declaration.

If you include a .cpp which has both declaration and definition into 2 different source files then that class will be doubly defined.

Each .cpp that the class is included into will compile fine into object files. However each class must have only 1 definition total or else you will not be able to link your object files together.

Brian R. Bondy
  • 339,232
  • 124
  • 596
  • 636
  • but wouldn't we should have the same problem now when we define one-liner functions in .h files? – gilbertc Mar 19 '10 at 18:21
  • @gilbertc: Inline functions (defined in a class definition, or explicitly inlined) can be reproduced multiple times, as long as they're all exactly the same. Other functions can't be. – David Thornley Mar 19 '10 at 18:29
1

The most important thing to understand about #include contrasted with other languages importing methods, is that #include COPIES the contents of that file where the #include directive is placed. So declaring and defining a class in the same file will create three things:

  • Significantly increase your compile
    times.

  • If your definitions are not inline you will get linker errors, since the compiler finds multiple
    definitions to the same functions

  • That would expose the implementation to the user, instead of only the interface.

That is why it is common practice to define large classes in separate files, and on some ocassions, really small classes with small implementations (like smart pointers) in one file(To also implicitly inline methods).

Ramon Zarazua B.
  • 7,195
  • 4
  • 22
  • 26
1

@Bill

I think it is important to underscore Bill's point:

Notice how much easier it can be to figure out how to use MyClass now. Header files serve an important purpose: they show people how to use your class.

the .h file being more or less the "public" doc to allow the understanding of how your class works in some ways conceptually--an Interface. Remember the source file should be thought of as proprietary. I remember learning a lot about how Unix worked in my early C/C++ days by reading header files. Also remember that inline function complexities should be no more than accessor's

Wintermut3
  • 191
  • 6
0

A big reason for a class to be defined in a cpp-file is that it isn't needed publically, it is just a helper function (like e.g. a functor). Some people seem to be afraid to put the complete class in the cpp-file, while that just shows your intent of only using the class there.

stefaanv
  • 14,072
  • 2
  • 31
  • 53
0

Files are usually the atoms of your version control system - if you partition things sensibly into different files, then it becomes possible for a team of developers to check out only the parts they each need to work on. Put everything in one file and you can't do that.

JustJeff
  • 12,640
  • 5
  • 49
  • 63