Let's face it. A lot of programming is spent managing resources. Memory, files, database connections, libraries and a variety of other things need to be allocated, deallocated and managed so your program doesn't come to a halt or crash the computer. Most memory management these days is handled for you because a large percentage of programs are written in languages or frameworks such as python, java, .NET, etc. that take care of memory management in most cases via garbage collection. However you still have problems with other types of resources, such as file handles, that you are required to take care of. Open a file or ADO connection and you have to close it. Load a dynamic library and you have to free it.
C++ provides a natural mechanism to manage resources and in my experience it gets very short shrift because of C++'s percieved issues with memory management. I believe that C++ offers a superior mechanism for resource managment, memory included, and here I will outline how I usually manage resources. By taking advantage of the fact that destructors are called when an object goes out of scope, you can practically guarantee that your allocated resources will be automatically cleaned up for you. Personally I miss this sort of deterministic destruction in just about every other language I have worked in. I've used my share of memory managed languages and while they often make writing code simpler, I have been bitten by garbage collectors not cleaning things up in a timely manner enough times that they tend to annoy me.
C++ has a reputation for being hard for a variety of reasons. The two main ones are
My belief is the issues people have with syntax are because they cut their teeth with C++ using MFC. Most of the complaints I have heard first hand about how hard C++ can be relate directly to MFC. The orginal incarnations of the MFC especially were poorly written wrappers around the Win32API. A close second for a lot of people's pain with C++ are directly related to BSTR's. Personally I think that BSTR is an abbreviation for BaSTaRd. Heck, Larry Osterman has had trouble with BSTRs.
The main memory management complaint I hear is that you have to manage it yourself. You can manage memory yourself if you are so inclined, but between the boost libraries, the STL and various Microsoft classes if you program on Windows, there are very few occasions where you should have to dynamically allocate and deallocate your own memory. Calls to new and delete should be the exception, not the rule.
String handling has also cropped up from time to time as a complaint. I find it hard to believe, but in this day and age lots of people still use C char arrays to manage strings. STL strings and the Microsoft CString class provide very good string implementations that don't have issues with buffer overflows and since you will be allocating objects, they will get cleaned up when the destructor is called. I cringe every time I see
char *myStr = new char[32];
and sadly I see it often enough that I feel it is worth mentioning. In my experience any time
you write code like this you are asking for trouble. Not only do you have to be sure that you call
delete to free the memory but you are now required to be sure that every operation on myStr is valid.
If you use strings like this you are probably making use of functions like printf and strcpy. These functions
increase the amount of work you have to do, are bug prone and are security risks.
Another perceived problem is calls to malloc. Other than outdated sample code I cannot honestly say when the last time was that I saw a call to malloc. This should even be a rarer thing than new or delete in most production C++ code. I will not be so naive as to say that you will never need to call these functions. I don't think I will need to, but maybe you will. I understand there are instances where people need these functions. I just don't program at that level. I would bet dollars to donuts that most of you don't, either. If you are calling malloc, STOP RIGHT NOW! If you don't call it, it isn't a problem.
Another commonly cited problem is using arrays. Native C arrays are contiguous blocks of memory, don't have bounds checking, etc. Like calls to malloc, these are usually best avoided. If you are using arrays as containers of objects, you should dump those and use STL vectors instead. Vectors grow dynamically, you don't have issues with accessing items out of bounds, and they perform reasonably will. Heck, there are plenty of game developers that recommend using vectors over arrays. Most game developers I know would rather gouge out their own eyes than waste a clock cycle. If the STL containers are good enough for them, they are probably good enough for you.
Managing memory in C++ has a bad reputation because people do the above things. In the great majority of cases there are better ways to handle the above issues by using the STL or other available classes and libraries that remove the pain of low level memory managment. If you need to manage memory at this level, you know who you are. You can safely ignore the above admonition. Generally speaking these 'big problems' have been solved for years now and you should not be afraid of them. You likely don't have to engage in any of the above practices. More importantly, memory isn't the only resource that needs management. It gets the most press because it is a problem that has been largely solved and it is an easy target for people that like working in managed environments. In contrast, C++ has a mechanism for effectively managing just about any resource, a capability that is lacking in most other languages.
What I am going to mention about managing resources in C++ is certainly not new but I see enough people who are impressed using this technique that I feel it is worth mentioning and showing an example of. Effectively managing resources in C++ is as simple as creating a class for the resource and doing any allocations, grabbing of handles, etc. you need in the constructor and then releasing the allocated resources in the destructor. This can obviously be done with memory but it can also be done with any other resource. Database connections, file handles, references to dynamically loaded libraries, just about anything where you read in the documentation "You have to call X_function to release the handle/resource/object when you are done with it".
In order for this to work easily you should, in addition to defining your classes in the above manner, not use new to allocate instances of your class. You should simply create an instance within the scope you will use the resource. Any time you call new you must be especially sure to call delete. By creating an instance in your desired scope you eliminate this worry because the destructor is guaranteed to be called when the object goes out of scope.
For an example we will create a simple class that can wrap calls to the cards.dll so you can draw cards. Here is our header file, cardDll.h
class simpleCard
{
public:
simpleCard();
~simpleCard();
int cardWidth;
int cardHeight;
void Draw(HDC dc, int x, int y, int card, int type, DWORD color){cardDraw(dc,x,y,card,type,color);};
private:
typedef BOOL (WINAPI *pfcdtInit)(int *, int *);
typedef BOOL (WINAPI *pfcdtDraw)(HDC, int x, int y, int card, int type, DWORD color);
typedef BOOL (WINAPI *pfcdtDrawEx)(HDC, int x, int y, int dx, int dy, int card, int type, DWORD color);
typedef BOOL (WINAPI *pfcdtAnimate)(HDC hdc, int cardback, int x, int y, int state);
typedef void (WINAPI *pfcdtTerm) (void);
HMODULE h_cardDll;
pfcdtInit cardInit;
pfcdtTerm cardTerm;
pfcdtDraw cardDraw;
pfcdtAnimate cardAnimate;
pfcdtDrawEx cardDrawEx;
};
And here is our simple cardDll.cpp. Notice in the constructor that we load the library and initialize all of the function pointers declared in the header. In the destructor we call the card dll's terminate function and then call FreeLibrary. Because we do this we can create an instance of this object, do what we need to do with the cards and then once the instance of the class (as long as we didn't use new to create it) goes out of scope we can be assured that the card libarary will get cleaned up and the library will get unloaded.
simpleCard::simpleCard()
{
h_cardDll = LoadLibrary(L"cards.dll");
cardInit = (pfcdtInit)GetProcAddress(h_cardDll, "cdtInit");
cardTerm = (pfcdtTerm)GetProcAddress(h_cardDll, "cdtTerm");
cardDraw = (pfcdtDraw)GetProcAddress(h_cardDll, "cdtDraw");
cardDrawEx = (pfcdtDrawEx)GetProcAddress(h_cardDll, "cdtDrawExt");
cardAnimate = (pfcdtAnimate)GetProcAddress(h_cardDll, "cdtAnimate");
cardInit(&cardWidth, &cardHeight);
}
simpleCard::~simpleCard()
{
cardTerm();
FreeLibrary(h_cardDll);
}
Enclosed is a Visual C++ 2005 express project that contains a .NET gui project that makes use of these files. The app simply paints 4 aces on the screen when you hit the button. Check out the button event handler to see a simple use of the simpleCard class.
C++ offers a very good mechanism for managing resources. This applies not only to memory but to handles of any sort, graphic resources, and so on. By taking advantage of constructors and destructors and properly scoping your variables you can ensure that a resource is used for the least amount of time possible and also ensure that it is properly managed. I believe that C++'s resource management is one of the most compelling reasons to use the language. In addition to allowing development in multiple paradigms and allowing you to create efficient programs it has mechanisms which allow you to very efficiently manage just about any resource.