TITLE: assignment via destruction and construction (Newsgroups: comp.lang.c++.moderated, 18 Nov 96) CUTHBERT: dacut@ugcs.caltech.edu (David A. Cuthbert) >> >> >One "philosophical" question about assignment operators: is there >> >anything wrong with typically using: >> >> > myclass& operator = (const myclass& rval) { >> > ~myclass(); >> > new(this) myclass(rval); >> > } CLAMAGE: Let's assume that the function protects against self-assignment. CUTHBERT: >> >as an assignment operator? I've been told that it isn't such a good >> >idea, but I don't quite see why. >> >> When you derive from myclass, the act of assigning a derived class >> will ordinarily call its base class assignment operators. (Even if >> you don't, users of your class might do it.) GREEN:In article 36r@netlab.cs.rpi.edu, Chelly Green >Then it is quite likely that the derived state won't be kept consistent. >As far as I'm concerned, this is a problem with the language ("slicing"). CLAMAGE: Evidently I didn't explain the problem well enough. Suppose we publish the following class: class myclass { public: virtual int f1(); // at least one virtual function myclass& operator=(const myclass&); // implemented as above ... }; Users are entitled to assume that the myclass assignment operator is implemented correctly, and that the class can be derived from: class derived : public myclass { ... }; If "derived" does not have its own assignment operator, the compiler will generate one like this: derived& operator=(const derived& rval) { if( this != &rval ) { myclass::operator=((myclass&)(*this)); // assign base class part ... // assign remaining members of "derived" } return *this; } Even if the author of "derived" writes an assignment operator, the obviously correct assignment operator will follow that schema, possibly with special handling for the current class members. What happens when we now write this: derived d1, d2; ... d1 = d2; First the base-class assignment operator gets called. Given the assignment operator for myclass, the base-class part gets destroyed, and a new base-class object is created in its place. In the usual C++ implementation, the derived-class vtable pointer gets replaced with a pointer to the base-class vtable pointer. Now we have a mess. In order to avoid the mess, we have to tell clients of "myclass" always to write their own assignment operator, and always to write it as destruction followed by copy-construction. A style that prevents normal language mechanisms and normally correct coding practices from working is pretty hard to defend. The abnormal requirements would have to solve a serious and otherwise intractable problem in order to be justified. On the other hand, if you write an assignment operator along the lines of what I suggested, the compiler-generated assignment operator will always do the correct thing for base classes, and user-written assignment operators can always assume that assigning base classes is correct. GREEN: > As I pointed out in another post, this very example (with >the x = x guard) was in the draft standard. CLAMAGE: Yes, in 3.8 "Object lifetime". But read the notes that precede the example. The draft does not claim that is a good example of how to write a general-purpose assignment operator. (IMHO, a different example should have been used, one which does not use bad coding practices.) Those paragraphs explain how an object can be destroyed and a new one of the same type can be safely created in its place, provided (among other things) that it is the most-derived object. That condition fails once you derive from the class, which is what I was pointing out. In short, the destroy-and-create model for a copy-assignment operator is safe if and only if - the class is not derived from; - copy construction and copy assignment ought to have the same semantics; - base-class copy construction and copy assignment do have the same semantics.