“Effective C++” : Part 4

With this post I consider the next four tips from Scott Meyer’s book on effective C++. This post brings us to the end of chapter 3 in the book which is dedicated to resource management. Resources in C++ are much like resources in any other language in that they are allocated and must be released to prevent things like memory leaks. Resources are more than just memory however so we have to pay attention things like database connections, sockets, file descriptors and mutexes. A resource is anything that once allocated can’t be used by someone else.

In early Linux systems it was quite common for an application to grab the sound subsystem and lock out other applications. The behaviour was undeterministic because every time the system booted and applications started a different application could grab and lock the sound subsystem.

13: Use objects to manage resources: What Scott is saying in this tip is that C++ has a number of mechanisms that can be used to control the allocation and deallocation of resources. If you design your code such that resources are allocated in an objects constructor and then de-allocated in the destructor, you can use the built in resource management that comes as part of C++. From there he goes on to talk about auto_ptr and shared_ptr which are objects that implement automatic resource management. The auto_ptr automatically destroys objects that it points to when it is destroyed and the shared_ptr is much like Apple’s ARC (Automatic Reference Counting). ARC automatically destroys and object when there is no one left pointing to it. ARC is kind of like garbage collection in Java but much more deterministic.

14: Think carefully about copying behaviour in resource managing classes: With this tip we need to consider what happens when a resource like a mutex are copied. This may sound like a stupid thing to do and one of the ways to avoid problems is to just not allow things like a mutex to be copied. If we consider the case however, where we want to share a resource, then we can implement a built in reference count to make sure that the resource is deleted when it is no longer being used. What this means is that the copy function for the resource in question does not copy the reference count which is global to all copies. The destructor of each copy will decrement the count and when the count reaches zero the resources get released.

15: Provide access to raw resources in resource managing classes: There are a number of reasons why programmers may need access to the raw resources that we are trying to protect. It could be legacy code or it could be an API that we don’t control. In any case, allowing access to raw resources is not the train wreck that it seems to be on the surface. What we have been discussing in the previous points is the allocation and de-allocation of resources, which is not the same as direct access. We can use mechanisms like reference counting to keep track of resources. Scott goes on to talk about simplifying the interface to objects that provide raw access. He talks about providing both explicit and implicit conversion of the raw resources. My take is that, implicit conversion is more dangerous and should be avoided in most cases.

16: Use the same form in corresponding uses of new and delete: This a simple but important point. If you create an object like and array of objects and you delete the array, the destructors for the objects within the array will not be called because delete has no idea how many objects are in the array. If you new an object like an array, it is important to tell delete so that it will deallocate all of the objects in the array. The way you do this is to say ‘delete[]’ instead of ‘delete’. The gotcha here is that calling delete[] on something that is not an array will buy you a ticket to the land of “undefined behaviour”.  So keep track of the things you allocate and delete them in the same way.

17: Store new’d objects in smart pointers, in stand-alone statements: This last point has to do with the flexibility compilers are given when evaluating operations in a single line of code. If we consider the line:

processWidget(std::tr1::shared_ptr<Widget>(new Widget),priority());

What we have here is the creation fo a Widget inside the call to processWidget. The compiler may decide to call priority first and in the process, if priorty throws an exception the reference to the new object is lost. By creating a seperate object (The code below) the object is in a stand alone statement which means we eliminate the uncertainty created by compiler options and the reference is not lost.

sdt::tr1::shared_ptr<widget>pw(new Widget);
processWidget(pw,priority());

So that brings us to the end of another informative chapter. Hopefully my very brief explanations have either gotten the point across or encouraged you to buy the book and read the examples. I must admit that I had to read the text several times to understand some of the more subtle points.

This entry was posted in General and tagged , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *