TITLE: exception safety and self-assignment (Newsgroups: comp.lang.c++) UNKNOWN1: > > > > Note that you should check in operator= for self assignment, or > > > > nasty things can happen. [snip] UNKNOWN2: > > > Note that if your assignment operator *must* check for > > > self-assignment, then the assignment operator is not > > > exception safe. This is something the FAQ overlooks. > > > Time to send an email to Marshall Cline, I guess... TITI: TiTi > > And why is that? An operator is always supposed to check for > > self-assignment. Always. A compiler cannot do that for you. WALKER: dwalker07@snet.net.invalid (Daryle Walker) [snip] > I agree that I don't see how a self-assignment check implies exception > non-safety. I would think that we basic s.a. check would be the > ultimate in exception safety: HYSLOP: Jim Hyslop I'd be interested in knowing what you think "exception safety" means. I suspect, from your comment, that it doesn't mean the same thing I think it does ;-) WALKER: > //================================================================ > MyType & > MyType::operator= ( const MyType © ) > { > if ( © != this ) { > // do the copying > } > return *this; > } HYSLOP: Consider this class (first a disclaimer: I've shown a char * member where std::string would be better, but std::string doesn't illustrate the point as clearly - in fact, it makes the entire assignment operator redundant, allowing us to use the compiler-generated operator ;-) class T { char *a; public: T& operator=(const T& other); }; A naive first try at implementing this function might be: T& T::operator=(const T& other) { delete [] a; a = new char[strlen(other.a)+1]; strcpy(a, other.a); return *this; } But as you recall from Meyers's "Effective C++", this implementation will have serious problems if both 'other' and 'this' are the same object. So you follow Meyers's advice and check for self-assignment: T& T::operator=(const T& other) { if (this != &other) { delete [] a; a = new char[strlen(other.a)+1]; strcpy(a, other.a); } return *this; } This assignment operator is now "copy-safe" (i.e. self-assignment won't cause problems). But *only* because you added the 'if' statement. "Ahhh, perfect," you say. Hold on! What happens if new throws an exception? In that case, your object is totally out of whack, because 'a' points to memory that has been deleted. You could add a line "a=NULL;" before the new statement, but even then it's still problematic because the object is in, basically, a useless state after you catch the exception. Therefore this assignment operator, although it is copy-safe, is not exception-safe. The solution is to perform all the exception-sensitive work before actually modifying the object itself: T& T::operator=(const T& other) { char *tmp = new char[strlen(other.a)+1]; strcpy(tmp, other.a); char *tmp2 = a; a = tmp; delete [] tmp2; return *this; } Or, taking advantage of the Standard Library: { std::auto_ptr(new char[strlen(other.a)+1]); std::copy(tmp, strlen(other.a)+1, other.a); std::swap(tmp, a); delete [] tmp; return *this; } This assignment operator is exception-safe, because the object itself is not modified until *after* any operations which might throw an exception have been performed. 'new' can throw, but the object won't be affected. std::copy might throw if we are using user-defined types instead of char, but again, the object being assigned to has not been affected, and since I've used auto_ptr, the memory allocated by new will not be leaked. std::swap won't throw, because (in this case) it's simple pointer assignments, similar to what I wrote in the previous implementation. delete[] won't throw for built in types, and user-defined types should always follow the rule "never throw an exception from a destructor". This assignment operator does not check for self-assignment, but it is still copy-safe. Checking for self-assignment is optional now - you can add it as an optimization (if the copying process is computationally expensive) or you can omit it since self-assignment is fairly rare, and you need to squeeze out the extra cycles the test takes up. In general, you will find that an assignment operator that is written so that it *must* check for self assignment will not be exception safe. Basically, I've just presented Item 38 from Herb Sutter's new book "Exceptional C++" (2000, Addison Wesley Longman). A good investment, IMO. [ Note there is a bug in that the pointer initializing auto_ptr() was allocated using new [] while the destructor for auto_ptr() will use delete instead of delete[]. Thanks for noticing this error is due to Roger Tragin (rtragin@wpine.com). -adc ] _______________________________________________ cpptips mailing list http://cpptips.hyperformix.com