TITLE: designing operator == (Newsgroups: comp.object,comp.lang.c++, 3 Dec 96) VANDAMME: Phil Vandamme > class ABC { > Boolean virtual operator==(const ABC & abc) { > if (&abc == this) return TRUE; > else FALSE; > } > } MARTIN: rmartin@oma.com (Robert C. Martin) This is an immensely presumptuous thing to do. You are presuming that two objects are identical iff their addresses are the same. In fact C++ does not guarantee this at all. In general objects may have many different addresses, all of which validly point to the object. Consider: class A{}; class B{}; class C : public A, public B {}; C c; A* ap = &c; B* bp = &c; assert(bp != ap); // the two pointers are not identical, yet // they refer to the same object. In general, it is a pretty bad idea to use the address of an object as it's identity. VANDAMME: > class A : public ABC { > // Inherits default operator== > } > } > > class B : public ABC { > Boolean operator==(const ABC & abc) { > // More sophisticated == verfication for class B, then on pointers. > // For example on string data member. > } > } MARTIN: I presume that this "more sophisticated" version uses a downcast to convert the ABC& to a B&. Please use 'dynamic_cast' to do this. Also, add this operator: bool B::operator==(const B&); so that in cases where the compiler knows you are comparing two B objects it doesn't have to upcast to ABC, use virtual deployment, and then downcast again to B. Thus, your virtual function will only be called in those cases where the calling code does not know the type of the objects it is comparing. VANDAMME: > class C : public ABC { > Boolean operator==(const ABC & abc) { > // More sophisticated == verfication for class C, then on pointers. > // For example on long data member. > } > } MARTIN: Same goes here. Use dynamic_cast, and also create an explicit operator for C so that most calls can use it. VANDAMME: > However, when B::operator==() is faultly passed a class C object, > the program will crash (as C is different from B) w/o any compiler > warning. MARTIN: If it is crashing (as in core dump) then you are probably downcasting like this: C& theC = (C&)abc; And if abc refers to a B, then this will crash very nicely. That's why you want to use dynamic_cast. bool C::operator==(const ABC& abc) { bool retval = false; C* theC = dynamic_cast(&abc); if (theC) { // sophisticated method for comparing 'theC' and 'this'. } return retval; } VANDAMME: > I thought of adding an Identify() member function to class ABC, and > using this in each operator==() to be sure they have identical > identifiers before accessing the data members to be tested equal, yet > then I'm forced to manually add identifiers to each derived class... > which is tedious for 1K of classes! MARTIN: That's why you want to use dynamic_cast. If your compiler does not support dynamic_cast, then you should get one that does. If you cannot get one that supports dynamic_cast, and you really have 1000 classes in the hierarchy, then you should abandon the operator== in the abstract base class. If you actually have less than 1000 classes, and have more like a dozen or two, then you can use the visitor pattern to simulate dynamic_cast. (See chapter 4 of my book "Designing Object Oriented C++ Applications using the Booch Method", Robert C. Martin, Prentice Hall, 1995) for details on how to do this.