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.
Programming style, also known as code style, is a set of rules or guidelines used when writing the source code for a computer program. It is often claimed that following a particular programming style will help programmers read and understand source code conforming to the style, and help to avoid introducing errors.
A programming style is a set of guidelines used to format programming instructions. It is useful to follow a style as it makes it easier for programmers to understand the code, maintain it, and assists in reducing the likelihood of introducing errors.
K&R style minimises the amount of vertical space which the code consumes, while maintaining that the syntax of the control structure itself does not share lines with its contents. Ensuring a control structure doesn't share lines with its content is important.
Some general advice on good coding styleGood code is self-explanatory – the need for comments should be minimal. Comments should be added to those parts of the code where explanations are needed. With code one should not be clever if it is not necessary – do things in an obvious straightforward way.
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 :/
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With