C++ is the greatest of all programming languages!
I believe that C++ is the best of all programming languages and cannot believe that a better language will be created any time soon.Google studies indicated that C++ is signficantly faster than any other language that they studied: See their article touting C++
Many people seem to think (mistakenly in my view) that everything should be easy. But learning to use C++ effectively is not -- but then learning to use the powers of God, were He to grant them to you, would not be easy either. With great power, comes great responsibility. C++ is no exception, but it really isn't all that hard to learn to use C++ like an expert. I have some ideas on the subject myself. See Tips and Tricks of the C++ professionals .
Some people seem to think that languages really don't matter. They are very mistaken. C++ has several features that greatly simplify the act of large scale software development. For example:
- C++ has three key kinds of code generation automation:
-
C++ has destructors! Destructors are functions that execute automatically
when a data object goes out of scope. This is an incredibly important feature
that was left out of Java and C#.
These languages do provide finalize blocks, of course, but this is merely a a mechanism by which a developer can manually add code that should occur at the end a given, specific, scope (curly brace block). This is pathetic approximation to destructors because if a given class object's destructor logic changes, the programmer is forced, in Java and C#, to track down all copies of the object in the source code and manually update the destructor logic. What was Bill Joy thinking when his team invented Java? Did they have no understanding of how easy it is for the compiler to inject destructor code into the program? And how hard it is to maintain finalize blocks? Apparently not.
Destructor logic is more important than just cleaning up memory when class objects go out of scope: A standard technique of ensuring that needed activities occur in a function scope is called "allocation equals initialization." Basically, you make a class whose only purpose is to have its destructor do something important -- such as restoring a global variable to a saved value. See example 1 below.
Here are a some well known destruction time behaviors that are automated in C++:
- freeing allocated memory to prevent leaks
- unlocking mutexes
- closing files
- Templates are the other form of automation built into C++. With templates you can both declare general strategies and specific instances. C++ templates aren't just "generics" like you find in Java and a few other languages. In C++, the templates are fundamental to the fabric of the language -- and they let you easily extend the language. Consider for example, this.
- Static variable initialization allows for automation of behavior at program startup time. Combining templates with static intialization allows you to make quick and easy changes to a large program that result in major program behaviors that are highly specific to particular classes.
-
C++ has destructors! Destructors are functions that execute automatically
when a data object goes out of scope. This is an incredibly important feature
that was left out of Java and C#.
- C++ is a truly compiled language -- and it has the performance to prove it.
Ok, Java and C# websites lie and say that their performance is 2 times or 5
times slower than C++ and that this is close enough. However, the example
programs that they use to demonstrate performance are really lies. None of
the example programs that I've ever seen actually forces the destruction of
objects created as it runs. In real programs, that run for hours or days
without exiting, freeing of memory is an essential component. If you
modify any of these examples in two ways you will see that the performance
of Java and C# are far worse than C++ -- more like 50 times slower, not
5 times slower. The two ways are these:
- instead of populating array_lists of type Integer for your test, make you own class type up -- let it emulate an integer if you like, just don't use class Integer.
- Use finalize blocks to force the destruction of the array_lists inside the loops to prevent memory growth
I don't know about the rest of you, but if I spend a lot of money on a faster machine, I meant to do more with it -- not just have sloppy programmers use up memory and time because it is now suddenly available. Not that speed is the be-all and end-all of program quality -- but it does matter.
- C++ has compile time type checking. Clearly this is a matter of concern for the end user -- why would you wait till runtime to check for an error that the compiler can tell you about before your product goes out the door?
- C++ has templates and the techniques are both typesafe and lightning fast and
work the same way for builtin types as it does for user defined classes. And
C++ templates can be instantiated differently for different template parameters
-- try that in Java or C#.
And templates allow class type specific optimizations with little or no effort. For example, suppose you wanted to write a generic sort algorithm that operates on any range of values. Most people will want to support the sorting of linked lists, vectors, and C++ arrays. With vectors and arrays the qsort() could be used to do the work -- if you wanted to. But with linked lists, other sorting algorithms must be used since qsort() doesn't understand linked lists. Template specialization will let you write a generic sort algorithm that handles linked lists and other non-contigous containers and also define a highly specialized, and super fast sort algorithm for arrays and vectors. The algorithm developer gives the compiler a few hints and it figures out which algorithm to select automatically.
See example 2 below for an explanation of how to do this.
Templates greatly aide in code automation -- although it does take practice to create them.
Some people claim that C++ is not a truly object oriented language. So? Truly object oriented languages, all of which make the Integer class be some sort of high level object, add enormous runtime costs that have no justification. The return on investment is almost zero when compared to C++ -- which has run time type identification as well as class inheritence with dynamic_cast<T> letting you defer type identification till run time.
In the early 1980s, I was sold on the value of object oriented programming -- even before having any real languages to use. I eventually began using object oriented techniques using the plain old "c" compiler. When C++ came around, I saw that I could better approximate true object oriented programming using constructors, inheritence, etc. I soon saw that perfect object orientation did not add much to my program's speed of implementation or its understandability. I saw no real improvement in those areas until about 2002 when the 3rd generation of C++ compilers were generally available on all major target platforms: all the unixes, ARMs, and Windows. It was around that time that you really could use both templates and runtime type identification the way they were meant to be used.
Some people claim that C++ is hard to use because it leaks memory. This belief persists because of a gross misunderstanding of the language: people are thinking about C and are mistakenly assuming that C++ works the same way.
It is almost trivial to prevent memory leaks. Here's how:
- Never use operator new except in the following scenarios:
- You are writing a constructor that has to allocate variable sized blocks -- or which allocates different kinds of blocks. Then, as soon as you finish typing the operator new call in the constructor immediately if not sooner, type in the delete call in the destructor.
- You are constructing an auto_ptr:
std::auto_ptr
ptr(new SomeClass); - You are initializing static memory
- Instead of using operator new all over the place, or even std::auto_ptr, use the STL container templates to store stuff. They already handle the memory for you. If you need pointers, you don't have to call operator new directly -- you can use a std::deque, a std::list, std::map, or most any STL container except vector to actually "own" the pointer. That is, instead of calling operator new, append an object to a global container and use the address of the object in the container. The vector container cannot be used for this purpose but most others can. std::deque has some additional limitations over and above list and map: you can't delete items from it without invalidating pointers into it.
To summarize: C++ compilers exist everywhere, provide signficant code automation tools, and with a few simple programming techniques most of the pain and suffering of using plain old C receeds into unpleasant memories.
Examples
Example 1: allocation equals initialization
In many real-world scenarios, variables are changed temporarily while something is done and the variable's original values are restored at the end. Here's a buggy example of trying to do this:// buggy code extern bool locked; // should be false most of the time bool f() { locked = true; if( function() == 0 ) return false; locked = false; return true; }Here, the code is written wrong -- if the function() returns 0, we should also set the locked variable to false. How did this mistake happen? In real world examples, this occurs when some future maintainer adds the if-statement to cause the early return from f(). This person doesn't know to check for all expected end actions and causes a very difficult to debug problem.
To solve this problem, use this code:
// Safe code extern bool locked; // should be false most of the time bool f() { Guard lockVariable(locked, true); // automatically saves and // restores the contents of // locked. if( function() == 0 ) return false; return true; }This works because class Guard's constructor saves the content of 'locked' and its destructor restores it. Here's an example implementation:
class Guard { public: bool *ward_; bool saved_value_; Guard(bool &ward, bool newValue) : ward_(&ward), saved_value_(ward) { ward = newValue; } ~Guard() { *ward_ = saved_value_; } };
Example 2: Writing specialized template algorithms
The modern approach to doing this is to simply rely on the language syntax for template function specifialization. There used to be a variety of really neat work arounds the let you solve the problem in a round-about fashion, but modern compiler's don't require them -- you just do this:template<class IteratorType> void sort(IteratorType first, IteratorType last) { // write the solution for the linked list class // and this handles } template<class T> void sort(T *first, T *last) { // write the solution for the C++ array case // -- maybe call qsort() } template<class T> void sort(std::vector<T>::iterator first, std::vector<T>::iterator last) { // write the solution for the stl vector case // -- you could call qsort // or use some algorithm out of a book. }The nice thing about this code is that the same function signature, sort(first,last), is used for all implementations -- and the compiler picks the right one for you.