Gotcha #40: Old-Style Casts
Don't use old-style casts. They simply do too much for and to you and are entirely too easy to use. Consider a header file:
// emp.h
// . . .
const Person *getNextEmployee();
// . . .
This header is used throughout the application, including the following section of code:
#include "emp.h"
// . . .
Person *victim = (Person *)getNextEmployee();
dealWith( victim );
// . . .
Now, any casting away of constness is potentially dangerous and unportable. Suppose, however, the author of this code is more clairvoyant than the rest of us and has determined that this particular use of the cast is correct and portable. The code is still wrong for two reasons. First, the conversion requested is much stronger than is necessary. Second, the author has fallen into the beginner's fallacy of depending on "secondary semantics": this code assumes that the observed, but unadvertised, behavior of the abstraction expressed by the getNextEmployee function will continue to be supported in the future.
Essentially, this use of getNextEmployee assumes that the function will never change after its initial implementation. Of course, this is not the case. Soon the implementer of the emp.h header file will recognize that employees are not people and will correct the design accordingly:
// emp.h
// . . .
const Employee *getNextEmployee();
// . . .
Unfortunately, the cast is still legal, although it has changed its meaning from modifying the constness of its object to changing the set of operations available on the object. In using the cast, we're telling the compiler that we know more about the type system than the compiler does. Originally, this may have been the case, but when the header file is maintained, it's unlikely that all uses of the header will be revisited, and our imperious command to the compiler will stand uncorrected. The use of the appropriate new-style cast would have allowed the compiler to detect the change in usage and flag the error:
#include "emp.h"
// . . .
Person *victim = const_cast<Person *>(getNextEmployee());
dealWith( victim );
Note that the use of a const_cast, while an improvement over an old-style cast, is still dangerous. We're still relying on the assumption that, under maintenance, the unadvertised—and perhaps accidental—collaboration between the functions getNextEmployee and dealWith that permit the const_cast will continue to hold.
|