This is the original GotW problem and solution substantially as posted to Usenet. See the book Exceptional C++ (Addison-Wesley, 2000) for the most current solutions to GotW issues #1-30. The solutions in the book have been revised and expanded since their initial appearance in GotW. The book versions also incorporate corrections, new material, and conformance to the final ANSI/ISO C++ standard. |
Object Identity Difficulty: 5 / 10"Who am I, really?" This problem addresses how to decide whether two pointers really refer to the same object. ProblemThe "this != &other" test (illustrated below) is a common coding practice intended to prevent self-assignment. Is the condition necessary and/or sufficient to accomplish this? Why or why not? If not, how would you fix it? Remember to distinguish between "protecting against Murphy vs. protecting against Machiavelli." T& T::operator=( const T& other ){
if( this != &other ) { // the test in question
// ...
}
return *this;
} SolutionShort answer: Technically, it's neither necessary nor sufficient. In practice it's probably fine and may even be fixed in the standard. Issue: Exception Safety (Murphy)If operator=() is exception-safe, you don't need to test for self-assignment. There are two efficiency downsides, however: a) if you can test for self-assignment then you can completely optimize away the assignment; and b) often making code exception-safe also makes it less efficient (a.k.a. the "paranoia has a price principle"). Nonissue: Multiple InheritanceThe problem has nothing to do with multiple inheritance, though some have suggested this in the past. The problem is a technical question of how the draft lets you compare pointers. Namely: Issue: Operator Overloading (Machiavelli)Since classes may provide their own operator&(), the test in question may do something completely different than intended. This comes under the heading of "protecting against Machiavelli" because presumably the writer of operator=() knows whether or not his class also overloads operator&(). Note that while a class may also provide a T::operator!=(), it's irrelevant since it can't interfere with this test. The reason is that you can't write an operator!=() that takes two T* parameters since at least one parameter to an overloaded operator must be of class type. Postscript #1Here's a "code joke". Believe it or not, it's been tried by well-meaning but clearly misguided coders: T::T( const T& other ) {
if( this != &other ) {
// ...
}
} Did you get the point on first reading? Postscript #2Note that there are other cases where pointer comparison is not what most people would consider intuitive. For instance: 1. As James Kanze points out, comparing pointers into string literals is undefined. The reason (which I didn't see stated) is that the draft explicitly allows compilers to store string literals in overlapping areas of memory as a space optimization. 2. In general you cannot compare arbitrary bald pointers using builtin operators <, <=, >, and >= with well-defined results, although the results are defined in specific situations (e.g., pointers to objects in the same array). The standard library works around this limitation by saying that the library functions less<> et al. must give an ordering of pointers, so that you can create, say, a map with keys of pointer type, e.g., map< T*, U, less<T*> >. |