One of the intended advantages of this type of string class is to allow code such as the following:
String a("abc"); String b(""); ... b=a;
This code is more straightforward than using C's standard string functions, and the String class takes care of managing the necessary memory to hold a string's data, regardless of its size. This "automated" memory management is done in the operator= function by releasing memory assigned to the target of the assignment (e.g., b, above), then allocating enough memory to hold the result of the string expression on the right-hand side of the assignment (e.g., a, above).
But what happens in the following code, where b is defined as a reference to a?
String a("abc"); String & b = a; // a and b refer to // the same string b=a;
Because both a and b refer to the same object, the release of b's memory actually releases a's memory as well-before the copy takes place-and the assignment fails. Note that a similar problem could occur in a plain C (or other language) function that didn't guard against modifying an output argument that might also be an input argument. But common goals of creating C++ classes are to simplify assignment and expressions for new object types and to "hide" memory management. Thus, you'll encounter more occasions where you have to watch out for unexpected ways that member functions, including overloaded operators, can get you in trouble.
In this simplified example, the solution is to avoid releasing the target string's memory, if it points to the same location as the source string. The revised member function is shown in Figure 8.4. To avoid problems like this, remember to implement assignment functions so that the same object can appear on both sides of the = operator. This rule can be quite challenging for classes that overload other operators in addition to =. Which leads to another way to minimize trouble: avoid overloading operators other than =.