Advertisement

Destroying everything in every path

February 06, 2009

Dan_Saks-February 06, 2009

Although I know both C and C++ quite well, I much prefer programming in C++. Features such as classes and access control help me partition my programs into components that are typically simpler and easier to use than they would be in C, with little or no loss of efficiency. Features such as constructors and destructors eliminate most of the resource management problems I encounter when I program in C.

Despite my preference, I understand and appreciate that many programmers still have to program in C for pragmatic reasons. As I've demonstrated in some of my columns over the years, conscientious C programmers can do a reasonably good job of mimicking classes with constructors.1, 2 It just takes more effort and discipline in C than it does in C++. Unfortunately, approximating classes with destructors can take a lot more discipline. Let me show you why.

Suppose widget is a type for an object that, among other things, manages a POSIX-style file referenced via an integer-valued file descriptor. In C, the definition for the widget type might look something like:

typedef struct widget widget;
struct widget
{
    int fd;     // a file descriptor
    // other data members
};

A function f that uses a local widget object might look like:

int f(void)
{
    widget w;
    w.fd = open(tmpnam(), O_RDWR);
    // do whatever else f does
    close(w.fd);
    return 0;
}

The statement immediately after w's declaration opens a temporary read-write file and assigns the associated file descriptor to w's fd member. The later call to close closes that temporary file just before the function returns, thus avoiding a resource leak. However, a leak could easily occur elsewhere in function f.

For example, suppose a conditional such as:

if (something bad happened)
    return -1;

were buried in the body of f. A call to f that terminates via this return-statement will fail to close w's file, causing a resource leak. Rewriting this conditional as:

if (something bad happened)
{
    close(w.fd);
    return -1;
}

eliminates the leak. It's not hard to write once you know you need to do it. Unfortunately, it's easy to overlook. It also forces you to duplicate code, which increases code size and invites future maintenance problems.

< Previous
Page 1 of 2
Next >

Loading comments...