Sadique
Sadique

Reputation: 22813

Operator= overloading in C++

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

Answers (5)

Fred Foo
Fred Foo

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

Matthieu M.
Matthieu M.

Reputation: 299730

@larsmans already answered your precise question, so I'll actually digress: This is some crappy code!

The issue here is 3-fold:

  1. You just duplicated the code of the copy constructor (somewhat)
  2. strcpy could be better replaced by strncpy, which does some bound checking
  3. It is not exception safe

1) 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 freed

Therefore 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 ?

  • we reuse the copy constructor for the actual copy
  • we reuse the swap function for exchanging values
  • we reuse the destructor for the clean-up

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

Erik
Erik

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

Tim
Tim

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

Kate Gregory
Kate Gregory

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

Related Questions