Errors when Defining Methods in Classes

C++ Default Methods

You must make sure that the default methods that C++ generates work well with your design. The default copy constructor and assignment operator tend to cause the most trouble to programmers. This was extensively covered previously.

Result: memory leak and/or dangling reference.

The Non-Virtual Destructor

If you intend others to inherit from your class, you must declare its destructor virtual. Otherwise, the derived class’ destructor may not be called in circumstances such as the one shown in the example.

Result: memory leak; possibly other problems.

Errors in Handling of Allocated Memory or Objects

Using Arrays Polymorphically

The ability to access a derived object through a reference or pointer of the base type is central to C++. It allows an important kind of polymorphism (literally, an ability to assume different forms) in which virtual method calls are made based on the actual (fully derived) type of the object, even though the pointer or reference is of the base type.

Unfortunately, this sort of polymorphism does not work with arrays of classes. C++ inherits its arrays from C ? there is basically no difference between an array and a pointer. As you index into an array of derived objects via a base-type pointer, the compiler is happily doing the pointer arithmetic using the size of the base class. The derived class, however, is almost certainly larger than the base class, due to extra members that have been added. Thus, the returned data is rarely a single valid object, but rather pieces of two adjacent ones.

Result: wild pointer (recall that dangling references are a special case of wild pointer).

Mistakes Using Casts

Casts have been likened to the infamous goto. This is not completely fair, but nevertheless, casts are rarely required in a well-designed C++ program. A cast will, for example, allow you to convert between two pointers to completely unrelated classes. This produces a wild pointer (recall that dangling references are a type of wild pointer). If you decide to use a cast, make it a very specific, limited part of your program ? preferably hidden away in the internal details of a class. Also, try to use the C++ style casts in preference to the older C style casts. The former come in several varieties, each capable only of a particular kind of conversion, generally making them far safer than the indiscriminate C casts.

Result: wild pointer.

Bitwise Copying of Objects

Mechanisms such as the memcpy function simply copy memory bit by bit. Not all objects, however, can be copied this way without damage. A C++ object is not a piece of dead data; it is a living, intelligent collection of data and functions. When an object is copied, for example, its copy constructor might need to allocate memory, notify other objects, etc. A bitwise copy circumvents these kinds of operations. This results in a seriously broken copy, in many cases.

Besides memcpy, other bit-by-bit copies can result from realloc (it makes such copies when it reallocates memory). An especially easy mistake to make is with variable argument lists. Passing a C++ object using a variable argument list results in a bitwise copy, and is therefore very dangerous (although passing pointers to objects will work, as long as you are mindful of the fact that va_arg uses casts).

Result: memory leaks and dangling references are both possible, as well as other problems.

Deallocation of Memory That Was Not Dynamically Allocated

It is important to make sure that any memory you free has, in fact, been dynamically allocated. Actions such as freeing local objects or deallocating memory more than once will be disastrous to your program. This also applies to using delete this; inside of a method of your class ? you must indeed be sure that the object has been dynamically allocated before you try to delete it.

Result: memory leaks and dangling references, as well as corruption of operating system data structures.

Mismatched Method of Allocation and Deallocation

Here is a list of common, matched allocation and deallocation methods.

new delete
new [] delete []
malloc() free()

It is a serious error to allocate with one method, and then use something other than the corresponding deallocation method to release the memory. In addition, note that malloc and free are not recommended in C++ ? they are not typesafe, and do not call constructors and destructors.

You should also never call an object’s destructor directly. The only exception to this is when you allocate the object using the placement new syntax (e.g. new (ptr_to_allocated_mem) MyClass;). In that case, you should call the object’s destructor when you are finished using it, especially before freeing the memory pointed to by pointer_to_allocated_mem (how you free the memory depends on how you allocated it in the first place).

Result: memory leaks and corruption of operating system data structures.

By George Belotsky Initially published on Linux DevCenter (http://www.linuxdevcenter.com/)