tags:

views:

390

answers:

8

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?

+1  A: 

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
but wouldn't we should have the same problem now when we define one-liner functions in .h files?
gilbertc
@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
+12  A: 

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
+1 -- Inlining everything also increases code size and breaks Intellisense in Visual Studio 2008.
Billy ONeal
@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
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
@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
@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
@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
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
@tJener: That's true, although many of Boost's libraries are header-only because they're heavily (or completely) template based.
Bill
+2  A: 

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.

Novelocrat
True -- unless you declare the methods inline.
Billy ONeal
+5  A: 

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
+1  A: 

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
+1  A: 

@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

Wintermute
A: 

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
A: 

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