Rules for Effective C++
Recently, however, my views have changed after reading Scott Meyer's book, Effective C++. In Meyer's book, he goes through every feature of C++ and shows you how you have to program with extreme care to avoid undefined behaviour. It seems like every modern feature that C++ has was specifically designed to help you shoot yourself in the foot.
I never realized this before, because I simply never use these dangerous features. In this article, I'll show you how to program in C++ safely.
What you may not know is that using exceptions in C++ makes a lot of code unsafe. In effect, it means
that you cannot use pointers. Take this code, for example:
If you are a C++ programmer and you use exceptions, you should see the obvious memory leak. If bar()
throws an exception, or calls any function that throws an exception, then obj will not be deleted.
It's actually quite hard to induce these functions to fail, so even if you did handle their failure, you probably wouldn't test it. Do you really want to be releasing code that you haven't tested? There are cases where it would be better for your program to crash, then to continue to operate in an undefined state.
However, there are times where I would check whether a memory allocation failed:
C++'s Broken Exceptions
Take exceptions, for example. Many programmers will tell you that they are a great idea. They let you
indicate errors when creating object, and avoid making you check return codes. You can handle errors in
the areas of the code that is prepared to handle them.
void foo()
{
MyObject* obj = new MyObject();
bar();
delete obj;
}
Steve's rules for effective C++
I have been programming in C++ for a decade, and I never realized these flaws until I read Meyer's
books. I find C++ to be just fine, and the reason for that is because I program in a style that doesn't
involve these pitfals. Here's how you can program in this way too:
Avoid exceptions
Exceptions will only leave you open to the memory and resource leaks. Don't use them. The exception to this rule is if you are programming in a style that doesn't use pointers, and everything is encapsulated into smart pointers.
Constructors should do nothing
Constructors have no way of returning an error code (unless you use exceptions, which are bad). That means that your constructors shouldn't do any real work. Don't try to open up a database connection, or call any functions that could fail.
Constructors should be used only to initialize data members.
Use copy constructors sparingly
Copy constructors are very error prone, because they are another thing that you have to remember to change if you add a data member. You're much better off if you don't allow copying at all. Just pass pointers around. If you must pass by value, then don't put anything in your object, like pointers, that will require special handling. That way, you can use the automatically generated copy constructor. Unlike you, the compiler will never forget anything.
Use malloc or new without checking the result
I used to write programs that checked every call to malloc() and new() for failure. In Microsoft C++, the new() operator will actually through an exception if it fails, so checking for NULL is useless anyway. Today's machines have gigabyes of memory, and you don't need to verify every call to malloc() or new().
When you are programming for an embedded device, however, it might be beneficial to not check the return code of malloc. This is when there should be enough memory in the heap for all operations. If your process silently fails when memory allocation fails, you might never catch a memory leak that is exhausting your heap. It is much better to fail catastrophically by trying to use the NULL pointer than silently failing.
I think there are critical problems with your proposed plan to not use exceptions. The post even contains the problem:
"Don't use exceptions [....] In Microsoft C++, the new() operator will actually throw an exception if it fails"
(Are you suggesting programming in C++ without using new?)
Others are right, use RAII (e.g. smart pointers) and your problems with exceptions will go away.
Copy constructors are handy in many cases. For example, in multithreaded programming (or simply for security) is it often better to take a copy than to coordinate access to the identical object. There are ways to write classes so that the copy constructor shares code with the other constructor, reducing the risk of not keeping them in sync.
IIRC correctly, in Standard C++ you can configure whether new throws or not. Also, IIRC, it's hard to get Visual C++ to obey this. Regardless, an additional reason to avoid the checks everywhere is to avoid code bloat. Relying on exceptions in this case is a good idea.
I still like C++, because it's a powerful and expressive language for building complicated and performant systems. I'm not saying you should write simple programs in C++, but when you're doing data mining suites or geospatial processing, C++'s facilities for abstraction are quite handy. Also templates have their uses (when not abused).