Reputation: 22813
In the book C++ Primer it has a code for C - style character arrays, and shows how to overload the =
operator in the Article 15.3 Operator =.
String& String::operator=( const char *sobj )
{
// sobj is the null pointer,
if ( ! sobj ) {
_size = 0;
delete[] _string;
_string = 0;
}
else {
_size = strlen( sobj );
delete[] _string;
_string = new char[ _size + 1 ];
strcpy( _string, sobj );
}
return *this;
}
Now i would like to know why is there the need to return a reference String &
when this code below does the same job, without any problem:
void String::operator=( const char *sobj )
{
// sobj is the null pointer,
if ( ! sobj ) {
_size = 0;
delete[] _string;
_string = 0;
}
else {
_size = strlen( sobj );
delete[] _string;
_string = new char[ _size + 1 ];
strcpy( _string, sobj );
}
}
Upvotes: 11
Views: 1361
Reputation: 363487
It supports the following idiom:
String a, b;
const char *c;
// set c to something interesting
a = b = c;
For this to work, b = c
must return an appropriate object or reference to assign to a
; it's actually a = (b = c)
according to the C++ operator precedence rules.
If you'd return the pointer this
, you'd have to write a = *(b = c)
, which doesn't convey the intended meaning.
Upvotes: 20
Reputation: 299730
@larsmans already answered your precise question, so I'll actually digress: This is some crappy code!
The issue here is 3-fold:
strcpy
could be better replaced by strncpy
, which does some bound checking1) and 2) are more stylistic than anything, but the 3) is a big concern
EDIT: as pointed out by @Jerry Coffin, this does not protected against self-assignment. That is if sobj
and _string
point to the same array of characters, you're in big troubles. The easy solution at the end of this post also covers this situation.
Not exception safe
Let's have a look at a portion of the code, namely, the else
part:
_size = strlen( sobj );
delete[] _string;
_string = new char[ _size + 1 ];
strcpy( _string, sobj );
What happens if, for some reason, new
throws ?
_size
has the value of the new string_string
points to the old pointer... which has been freedTherefore not only the object is left in an unusable state (half of its data from the new object, half from the old), but it cannot even be destroyed (unless the destructor leaks... ?)
Adding exception safety, the hard way
_size = strlen( sobj );
delete[] _string;
try {
_string = new char[ _size + 1 ];
} catch(...) {
_size = 0; _string = 0;
throw;
}
strcpy( _string, sobj );
Okay, it's really bottom-line, but it brings us the Basic Exception Guarantee: no functional guarantee, but a guarantee that the code is technically correct (no crash, no leak).
Adding exception safety, the easy way: The Copy-And-Swap idiom
Find a more complete description at: What is the copy-and-swap idiom ?
void swap(String& lhs, String& rhs) {
using std::swap;
swap(lhs._size, rhs._size);
swap(lhs._string, rhs._string);
}
String& String::operator=(String other) { // pass-by-value
swap(*this, other);
return *this;
}
How does it work ?
And it's even better than the previous version too, for now we have the Strong Exception Guarantee: it's transactional, thus if it fails, the string we assigned to is unchanged (as if nothing had happened).
More about Exception Guarantees.
I am a bit daunted that a C++ tutorial would promote such dubious code, pray tell me it's an example of what not to do :/
Upvotes: 8
Reputation: 91260
By convention, you're able to do e.g:
int x, y, z;
x = y = z = 0;
if ((x = 1) != 0) { ... }
In order to keep these semantics for instances of your class, you need to return *this
- allowing chained assignment. If you return just this
you'd be returning a pointer to your instance, and chained assignment wouldn't work - there's no operator=
for String *
, there is one for String
Upvotes: 0
Reputation: 9172
Convention. Built-in types do it, auto-generated assignment operators do it, it allows you to do this:
a = b = c;
or this:
if ( foo = come_function() ) {
// use foo here
}
Upvotes: 3
Reputation: 18944
The returning from operator = is so you can chain things. See here without strings:
x = y = 2;
The y=2
part of that line returns 2. That's how x gets a value. If op= returned void, you couldn't chain = operations.
Upvotes: 0