13

I'm an old (but not too old) Java programmer, that decided to learn C++. But I have seen that much of C++ programming style, is... well, just damn ugly!

All that stuff of putting the class definition in a header file, and the methods in a different source file- Calling functions out of nowhere, instead of using methods inside classes. All that just seems... wrong!

So finally, is there any reason for me to continue with this massacre to the OOP, and anything that is good and righteous in programming, or can I just ignore that old-fashioned C++ conventions, and use my good Java programing style?

By the way I'm learning C++, because I want to do game programing.

Here is an example:

In an C++ website I found a Windows implementation:

class WinClass
{
    public:

        WinClass (WNDPROC wndProc, char const * className, HINSTANCE hInst);
        void Register ()
        {
            ::RegisterClass (&_class);
        }

    private:

        WNDCLASS _class;
};

That class is located in a header file and the constructor:

WinClass::WinClass (WNDPROC wndProc, char const * className, HINSTANCE hInst)
{
    _class.style = 0;
    _class.lpfnWndProc = wndProc;  // Window Procedure: mandatory
    _class.cbClsExtra = 0;
    _class.cbWndExtra = 0;
    _class.hInstance = hInst;           // Owner of the class: mandatory
    _class.hIcon = 0;
    _class.hCursor = ::LoadCursor (0, IDC_ARROW); // Optional
    _class.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // Optional
    _class.lpszMenuName = 0;
    _class.lpszClassName = className;   // Mandatory
}

Is located at a .cpp source file.

What I could just do is:

class WinClass
{
    public:
        WinClass (WNDPROC wndProc, char const * className, HINSTANCE hInst)
        {
            _class.style = 0;
            _class.lpfnWndProc = wndProc;  // Window Procedure: mandatory
            _class.cbClsExtra = 0;
            _class.cbWndExtra = 0;
            _class.hInstance = hInst;           // Owner of the class: mandatory
            _class.hIcon = 0;
            _class.hCursor = ::LoadCursor (0, IDC_ARROW); // Optional
            _class.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // Optional
            _class.lpszMenuName = 0;
            _class.lpszClassName = className;   // Mandatory
        }

        void Register ()
        {
            ::RegisterClass (&_class);
        }

    private:
        WNDCLASS _class;
};

And now the constructor is inside its class.

asantacreu
  • 192
  • 1
  • 3
  • 18
Diones
  • 1,425
  • 2
  • 19
  • 26
  • 7
    If you are old, I'm already dead. (or you have supplied a wrong age in your profile) – Toon Krijthe Dec 16 '08 at 21:50
  • I'm not sure why separate header files and implementation files is a massacre of OOP, but do as you please... – Tim Dec 16 '08 at 21:51
  • 5
    I am jsut glad we don't masacre the OO concepts with sticking get() set() methods everywhere that destroy the whole concept of incapsulation :-) – Martin York Dec 16 '08 at 23:07

16 Answers16

24

In addition to what others have said here, there are even more important problems:

1) Large translation units lead to longer compile times and larger object file sizes.

2) Circular dependencies! And this is the big one. And it can almost always be fixed by splitting up headers and source:

// Vehicle.h
class Wheel {
    private:
        Car& m_parent;
    public:
        Wheel( Car& p ) : m_parent( p ) {
            std::cout << "Car has " << m_parent.numWheels() << " wheels." << std::endl;
        }
};

class Car {
    private:
        std::vector< Wheel > m_wheels;
    public:
        Car() {
            for( int i=0; i<4; ++i )
                m_wheels.push_back( Wheel( *this ) );
        }

        int numWheels() {
            return m_wheels.size();
        }
}

No matter what order you put these in, one will always be lacking the definition of the other, even using forward declarations it won't work, since in the function bodies are using specifics about each class's symbol.

But if you split them up into proper .h and .cpp files and use forward declarations it will satisfy the compiler:

//Wheel.h
//-------
class Car;

class Wheel {
private:
    Car& m_parent;
public:
    Wheel( Car& p );
};

//Wheel.cpp
//---------
#include "Wheel.h"
#include "Car.h"

Wheel::Wheel( Car& p ) : m_parent( p ) {
        std::cout << "Car has " << m_parent.numWheels() << " wheels." << std::endl;
}

//Car.h
//-----
class Wheel;

class Car {
private:
    std::vector< Wheel > m_wheels;
public:
    Car();
    int numWheels();
}

//Car.cpp
//-------
#include "Car.h"
#include "Wheel.h"

Car::Car() {
        for( int i=0; i<4; ++i )
            m_wheels.push_back( Wheel( *this ) );
}

int Car::numWheels() {
        return m_wheels.size();
}

Now the code that actually has to know specifics about the second class can just include the header file which doesn't need to know specifics about the first class.

Headers just provide the declarations while source files provide the definitions. Or another way to say it: Headers tell you what is there (what symbols are valid to use) and source tells the compiler what the symbols actually do. In C++ you don't need anything more than a valid symbol to begin using whatever it is.

Trust that C++ has a reason for this idiom, because if you don't you will make a lot of headaches for yourself down the line. I know :/

Adam
  • 25,966
  • 23
  • 76
  • 87
  • 4
    this is the only answer i found that actually is not like "java does it wrong; separation is good" but actually provides reason for why it is that it is. You could mention that c++ is compiled in one-pass, and that there are special constructs supporting that style (forward-declaration) – Johannes Schaub - litb Dec 16 '08 at 22:47
  • 3
    and that there doesn't exist a module concept, and thus header files are required to provide the information normally found in meta information about modules. – Johannes Schaub - litb Dec 16 '08 at 22:48
  • 3
    those two points roughly correspond to your two points, but your points explain the syntoms (large headers (because of missing modules) / circular dependencies (remember: compiling two pass, there are no circular dependency problems anymore)). – Johannes Schaub - litb Dec 16 '08 at 22:49
16

When the code builds, the C++ preprocessor builds a translation unit. It starts with a .cpp file, parses the #includes grabbing the text from the headers, and generates one great big text file with all the headers and the .cpp code. Then this translation unit gets compiled into code that will run on the platform you're targeting. Each translation unit ends up as one object file.

So, .h files get included in multiple translation units, and a .cpp file just gets included in one.

If .h files contain lots of stuff (including implementation), then the translation units would be correspondingly bigger. Compile times would increase, and when you change anything in a header... every translation unit that uses it would need to be recompiled.

So.. minimizing stuff in .h files drastically improves compile time. You can edit a .cpp file to change a function, and only that one translation unit needs to be rebuilt.

To complete the story on compiling...
Once all the translation units are build into object files (native binary code for you platform). The linker does its job, and stitches them together into you .exe, .dll or .lib file. A .lib file can be linked into another build so it can be reused in more than one .exe or .dll.

I suppose the Java compilation model is more advanced, and the implications of where you put your code have less meaning.

Scott Langham
  • 58,735
  • 39
  • 131
  • 204
  • Please provide a reference to large scale programming and strategies for reducing compilation times for the sake of completeness. – questzen Dec 16 '08 at 23:10
  • 1
    There's this: http://www.amazon.com/Large-Scale-Software-Addison-Wesley-Professional-Computing/dp/0201633620 – Scott Langham Dec 16 '08 at 23:19
  • 1
    And this: http://stackoverflow.com/questions/373142/what-techniques-can-be-used-to-speed-up-c-compilation-times – Scott Langham Dec 16 '08 at 23:25
11

.h and .cpp files often separate declarations from definitions, and there is some kind of order here, and conceptual encapsulation. headers have the 'what', implementation files have the 'how'.

I find having everything in one file in java to be disorganized. So, each to their own.

C++ conventions and idioms all have well thought out reasoning, just like any other language does for the idioms its community adopts.

I think it's best to java (as a verb) when you write Java, and to C++ when you C++! You'll appreciate why with experience of the language. Same as switching to any new language.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
Scott Langham
  • 58,735
  • 39
  • 131
  • 204
10

Most answers are about the separation of headers and compilation units. I agree with most, you must use it because it is more efficient, cleaner, more user friendly to coworkers or just because... (and you will notice the compilation time advantage not too long from now).

Anyway I just wanted to post on the other part of the question: is C++ an OOP masacre?

C++ is a multiparadigm language. It allows procedural, object oriented and generic programming. And that is a virtue, not a defect. You can still use pure OOP if you wish, but your code will surely benefit from learning and knowing when to use other paradigms.

As an example, I dislike utility classes, where all member functions are static, and there is no data. There is no reason to create a utility class more than just grouping together a set of free functions under a common name. You must do it in Java, as it insists on pure OO syntax which, as commeted by Tom, is not the same as real OO. In C++ defining a namespace and a set of free functions offers a similar solution without you needing to lock the creation of objects by declaring a private constructor or else allowing users to instantiate non-sense objects from an empty class.

To me, experience (I was once a Java-only programmer) has taught me that there are different tools that better fit different problems. I am yet to find a golden hammer and the advantage of C++ is that you can choose a different hammer for each different task, even in the same compilation unit.

Correction: litb informs me in a comment that WNDCLASS is a struct in the win32 API. So that the criticism of the class not using initialization lists is plain nonsense and as such I am removing it.

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    well i think WNDCLASS is a struct of the Win32 API. it doesn't have a constructor that takes the parameters so he cannot use an initializer list :) – Johannes Schaub - litb Dec 16 '08 at 22:58
  • 1
    Nitpick - I wouldn't say Java enforces "pure OO", I would say it enforces "pure OO syntax". It's a common fallacy that Java == OO just because of the objects (^: – Tom Dec 16 '08 at 23:18
8

Find a style that works for you, just like everyone else did. Nobody is forcing you to use one of the "ugly" styles, unless it's your employer enforcing a guidelines document. ;-)

Though keep in mind that placing member function definitions inside the class as opposed to outside has different semantics. When you define a member function inside the class, it's implicitly inline, for better or worse.

  • 1
    I think the poster is just asking if there is a reason other than tradition that C++ classes are usually broken into header and implementation files. – Brian Dec 16 '08 at 22:02
8

That's called seperating interface and implementation. That allows clients to figure out how to call your class without having to wade through all the code too. Frankly I think it's practically insane that people could consider it "wrong".

However, you will be quite pleased to find out that many C++ developers agree with you on that score. So go ahead and put your entire implementation in *.h files. There's a school of style out there that agrees with you.

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
  • 1
    I think you could consider said separation it insufficient in C++. The amount of information necessary to expose for class definitions and template functions is extraordinary, compared to C-style interfaces (or non-template pimplized C++). Given that, I can see the draw towards header-only libs. – Tom Dec 16 '08 at 23:12
6

Calling functions out of nowhere, instead of using methods inside classes; All that just seems... wrong!

So finally, is there any reason for me to continue with this massacre to the OOP

Well, calling functions that don't belong to classes isn't OOP -- it's procedural programming. Thus, I believe you're really having a difficult time breaking out of the OOP mindset. (Certainly C++ has many ills, but procedural programming isn't one of them.)

C++ is a multi-paradigm language, not just an OO langage. Templates are a form of generic programming which can be applied to procedural, OOP, and meta-programming paradigms of C++. With the next C++ standard, you'll see some functional paradigms added as well.

All that stuff of putting the class definition in a header file, and the methods in a different source file;

This has its origins from the C programming language, back in the 70's. C++ was designed to be backwards compatible with C.

The D programming language is an attempt to fix many of the ills of C++ (and as I said earlier there are many), but maintain the good features of C++ -- including all the various paradigms C++ supports: procedural, OOP, metaprogramming, and functional.

If you want to break out of the OOP mindset, try D! Then, when you get a feeling for how to mix and match the different paradigms, you can (if you so desire) come back to C++ and learn to deal with its syntax and other ills.

P.S. I use C++ daily and I am a fan of it -- it is my favorite programming language. But as any experienced C++ programmer knows, C++ does have its ills. But once you master the different paradigms and know how to work around C++'s ills, you'll have an incredibly powerful tool (for that is all a language is) at your disposal.

Good luck in your adventures!

Uhall
  • 5,673
  • 7
  • 24
  • 19
  • 1
    Agreed. In any case, Java has classes consisting only of static methods (java.lang.Math, for example). Having a fake class as a place-holder doesn't make that any "more OOP" than free functions in C++, and some of them are less powerful than the C++ equivalents because they're not generic. – Steve Jessop Dec 17 '08 at 01:03
3

If you want to implement all your methods inside your class definitions, you will have big monster header files (it's not a good idea to declare classes in cpp files) and possibly only one cpp file (the one that has your main function).

So it's a matter of convenience.


Edit:

I find the declaration-implementation separation actually helpful. When I have monster classes with ~150 methods whose implementation is generated using lots of preprocessor tricks, I often want to be able to tell the difference between...

  • what the class can do (its interface, given by its declaration)
  • how the class does it (its implementation)

Not being able to do that in either C# or Java is quite frustrating, especially when I don't have Intellisense at hand.

isekaijin
  • 19,076
  • 18
  • 85
  • 153
  • 1
    I could see it with the lack of intellisense, but I love the navigational features in C# like collapsing regions and the drop down menu of all of the methods in the class. – Steven Behnke Dec 16 '08 at 22:11
  • 1
    When I use Visual Studio, I use regions a lot, no matter whether I'm using C++ or C#. I don't know any Java IDE that offers such a possibility. – isekaijin Dec 16 '08 at 22:16
3

That's not ugly at all. I would say it is... mhhh different. Bringing your old style to a new platform, that's ugly (I have had a lot of discussions about this with Mr. Skeet).

Remember Java was defined years after C++, so it is reasonable they've fixed some constructs (in the same fashion C# learn from Java).

So, I would suggest to keep in that C++ style.

Think about this. Header files are like interface declarations and cpp files are implementation.

I think C++ does not have "interface" keyword, and this is the way you separate the interface of a class from its implementation. Similar to this in Java:

public interface Some { 
    public void method();
}

public class SomeImpl implements Some { 
    public void method(){}
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
3

If you are writing a C++ DLL and you put any code in the headers, you will end up with a hard to debug mess. The DLL will contain the code from the .cpp files but the users of the DLL will have some of the code inlined into themselves.

This is really bad in Windows where you run into things like different heap allocators in different parts of the program and if you change the implementation, callers of your DLL will be using old code from the inlined headers.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
3

Separate declaration and definition is the least of the differences between C++ and Java. The major difference in modern C++ is the importance of "value semantics".

Due to the lack of garbage collection but the excellent support for building types that behave like self-contained values, good C++ style involves writing well-behaved value types, with consistent construction, copy-construction, assignment, swap and destruction.

Swap in particular is a very important one to remember because it's not directly supported by the language but it really should be there in any value-like type.

Look at how the standard C++ library works, and how it expects your types to behave.

Try to aim (not always possible) for a total absence of naked pointers or uses of the new operator. Hide such details inside classes that ensure they are used correctly, wrapping them up in value semantics.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
3

Like a stereotypical programmer who programs in one specific paradigm, you decided that certain things that aren't familiar with you is just ugly.

C++ is a different language, it's multiparadigm, and it has lots of carry-overs from C. As other have stated, that's just the way it is.

Header files are there for the compiler to check basic syntax without knowing the implementation. If you're a heavy Java programmer, you should be fairly familiar with the whole program to an interface concept. Think of the header file is where the interface is.

If you don't believe me, go look at some Mods out there for games. It's entirely possible to find the CryEngine2's header file somewhere because Crysis mods would need to talk to it, but it doesn't need to understand how it works. The header files will define how ultimately a calling function should setup the stack to call another function.

Not to be condensending, but I think what you really need is to program in something completely different. Research other paradigms, and find some languages, and start writing some toy applications. At first, things would look ugly, but you'll eventually see the advantages.

I said the same thing about functional programming too. Now I complain about how PHP butchered functional programming with arrays.

Calyth
  • 1,673
  • 3
  • 16
  • 26
2

I think many programmers cut their teeth with MicroSoft products (and their example code) and/or programming for Windows API and the early Microsoft coding conventions which were used in the code fragment in your question (I.e. Hungarian notation, capitalization of defined types, etc..). I loathe looking at source code from MicroSoft which looks like it has been run through a crosscut paper shredder and glued back together. But an ugly coding convention is not a function or reflection of the C++ language which I find no more beautiful or ugly than most other languages.

Actually, I find the C++ syntax with minimal keywords, curly braces and rich set of symbolic operators serves to not distract from or crowd out the important stuff: my variables, my type defs, my methods; which, of course, allows me to make the most beautiful code of all :-)

Roger Nelson
  • 1,882
  • 2
  • 15
  • 21
1

I worry from the tone of your question that you may be reading some bad C++ code in learning C++. Well-written code is usually not ugly in any language. As a starting point, you might try the online C++ FAQ, especially the chapter on learning C++.

Michael Kristofik
  • 34,290
  • 15
  • 75
  • 125
1

The style you program in is up to you. Just make sure you understand that "ugly" style others use.

Jim C
  • 4,981
  • 21
  • 25
1

If you want to do game programming, you probably want to work with other C++ developers, and this means you have to do things in a way they'll understand. If you hope to have any collaboration at all, your code will have to be in a reasonable C++ style. If you intend to be a lone developer, and have your code essentially die with you, use any style you like.

Moreover, C++ and Java are two different languages, and need to be used in different ways. There are reasons for the header files (think of it as declaring the interface), for example.

David Thornley
  • 56,304
  • 9
  • 91
  • 158