C++
Tips
Minimize includes in headers
You should try to include the minimum number of other headers in your own header files. There are several reasons for this:
- If all header files include many other headers files, which each in turn include many other header files, then compilation times can get very long. And if you change one header, you may suddenly find that practically your entire project needs to be recompiled.
- By including many headers, which in turn include many other headers, you may be importing stuff you don't want and have no idea about. For example, you may be getting macro definitions that cause unexpected behaviour. This can be very hard to track to debug is there is a vast chain of headers to trace.
The alternative to including the header for functionality that you want to make use of, is to forward declare the objects, and then include the header in the .cxx file. (Including headers in .cc files is more innocuous, because .cxx files aren't in turn included by other files.
Here is an example. First the "before" case, where we have a certain type of header included.
// File "myClass.h" - example of including another header.
#include "vtkImage.h"
class myClass
{
public:
myClass(); // Constructor
~myClass(); // Destructor
protected:
vtkImage image; // An image object in our class
};
Now the header file using forward declarations instead of including another header file:
// File "myClass.h" - example of using forward declaration.
class vtkImage // Forward declaration
class myClass
{
public:
myClass(); // Constructor
~myClass(); // Destructor
protected:
vtkImage* image; // An image object in our class
};
Note that now we have a pointer instead of an actual instance of vtkImage. A forward declaration allows you to use only pointers and references to the foward-declared objects. This is because the compiler does not yet know how large the object is, but it does know how large a pointer is.
Now because we have only a pointer, we need to create an instance of our image object in the cxx file, and of course get rid of it too. One way to do this is in the constructor and destructor.
// File "myClass.cxx"
myClass::myClass()
{
image = vtkImage::New(); // Create a vtkImage object
}
myClass::~myClass()
{
image->Delete();
}
In this example we are using the VTK static New and Delete methods, because we have a VTK object. For other objects you will probably use the plain vanilla C++ new and delete commands.
Minimize use of macros
Because your header file may be included in files created by other people, you absolutely want to minimize or eliminate the use of macros, because they could cause unexpected issues with someone else's code. C++ provides many ways to avoid using macros. For example
// A truely evil macro - never do this #define max(x,y) (((x)>(y)) ? (x) : (y)))
In this case you can use a templated in-line function to achieve the same functionality with the same runtime speed:
template <typename T>
inline T max (T x, T y)
{
return (x>y) ? x : y;
}
This version is typesafe, and can be overridden for specific types where required. It also illustrates another tip, which is #Don't reinvent the wheel, because there already exists a templated in-line max function in the C++ standard library.
Prefer iostreams to printf and scanf
Most developers are familiar with the C-style printf statement syntax, for example:
printf ("The result is %d\n", i);
The drawback of this approach is that the formatting code depends on the type of i. Even worse, it may vary depending on the OS and whether we are using 32 bit or 64 bit. For example. maybe that formatting code should be "%ld" or "%lld" or "%Ld". The first is for long ints, the second for long long ints, and the third is system dependent. Which would you use for a 64 bit length integer? The answer to that is system dependent. Since we compile our code on many systems, and both as 32 bti and 64 bit, it is important to avoid this conundrum. (In templated code this situation is even worse).
The C++ iostream equivalent to the above is:
std::cout << "The result is " << i << "\n";
Now the compiler will make the right conversion for us. More complicated printf style formatting can also be converted to iostrings, for example:
printf ("The result is %.3f\n", x);
becomes
std::cout << "The result is " << setprecision(2) << x << "\n";
Don't ignore compiler warnings
Often, your program will work correctly despite compiler warnings. Nevertheless, you should fix your code, if at all possible, so it does not generate any warnings. The warnings are there for a good reason. Very often they indicate a potential bug that could be hard to debug if it occurs. Other times, code that compiles and runs correctly, but with warnings, on one system, will compile but run incorrectly on another system. Finally, if your code generates many many warnings, you will cease to pay attention to them at all. New warnings, indicative of real problems, will then be lost in the noise.
Put comments in the appropriate place
Never put 'using namespace' in a header
Don't typedef away indirection
Don't reinvent the wheel
boost
-string manipulation - platform-independent file path handling - fixed-length integers (e.g 64 bit) See next section.
numerical especially important: lapack