1

I'm trying to understand the purpose of overloading some operators in C++.
Conceptually, an assignment statement can be easily implemented via:

  • Destruction of the old object followed by copy construction of the new object
  • Copy construction of the new object, followed by a swap with the old object, followed by destruction of the old object

In fact, often, the copy-and-swap implementation is the implementation of assignment in real code.

Why, then, does C++ allow the programmer to overload the assignment operator, instead of just performing the above?

Was it intended to allow a scenario in which assignment is faster than destruction + construction?
If so, when does that happen? And if not, then what use case was it intended to support?

2
  • Operator in general allow us to get closer to the target domain. Commented Feb 15, 2013 at 18:14
  • double converter::operator=(int value); Commented Feb 15, 2013 at 18:38

6 Answers 6

2

1) Reference counting

Suppose you have a resource that is ref-counted and it is wrapped in objects.

void operator=(const MyObject& v) { this->resource = refCount(v->resource); } // Here the internal resource will be copied and not the objects. MyObject A = B; 

2) Or you just want to copy the fields without fancy semantics.

void operator=(const MyObject& v) { this->someField = v->someField; } // So this statement should generate just one function call and not a fancy // collection of temporaries that require construction destruction. MyObject A = B; 

In both cases the code runs much faster. In the second case the effect is similar.

3) Also what about types... Use the operator to handle assigning other types to your type.

void operator=(const MyObject& v) { this->someField = v->someField; } void operator=(int x) { this->value = x; } void operator=(float y) { this->floatValue = y; } 
Sign up to request clarification or add additional context in comments.

Comments

2

first, note that "Destruction of the old object followed by copy construction of the new object" is not exception safe.

but re "Copy construction of the new object, followed by a swap with the old object, followed by destruction of the old object", that's the swap idiom for implementing an assignment operator, and it's exception safe if done correctly.

in some cases a custom assignment operator can be faster than the swap idiom. for example, direct arrays of POD type can't really be swapped except by way of lower level assignments. so there for the swap idiom you can expect an overhead proportional to the array size.

however, historically there wasn't much focus on swapping and exception safety.

bjarne wanted exceptions originally (if i recall correctly), but they didn't get into the language until 1989 or thereabouts. so the original c++ way of programming was more focused on assignments. to the degree that a failing constructor signalled its failure by assigning 0 to this… i think, that in those days your question would not have made sense. it was just assignments all over.


typewise, some objects have identity, and others have value. it makes sense to assign to value objects, but for identity objects one typically wants to limit the ways that the object can be modified. while this doesn't require the ability to customize copy assignment (only to make it unavailable), with that ability one doesn't need any other language support.


and i think likewise for any other specific reasons one can think of: probably no such reason really requires the general ability, but the general ability is sufficient to cover it all, so it lowers the overall language complexity.


a good source to get more definitive answer than my hunches, recollections and gut feelings, is bjarne's "the design and evolution of c++" book.

probably the question has a definitive answer there.

2 Comments

The reference to Bjarne's book is certainly the ultimate answer. But although it didn't occur to me at first either, I suspect that C compatibility played an important role; the compiler provided assignment operator does exactly what C would do when assigning a struct. (Almost: it does member by member copy, where as C would do bitwise copy. But there's no difference between member by member and bitwise copy. And the very earliest versions of C++ did bitwise copy.)
Also, the ability to have objects on the RHS that are of different types probably plays a role.
2

Destruction of the old object, followed by copy construction of the new, will not usually work. And the swap idiom is guaranteed not to work unless the class provides a special swap function—std::swap uses assignment in its unspecialized implementation, and using it directly in the assignment operator will lead to endless recursion.

And of course, the user may want to do something special, e.g. make the assignment operator private, for example.

And finally, what is almost certainly an overruling reason: the default assignment operator has to be compatible with C.

2 Comments

Definitely plausible, but not wholly convincing (since they could've just made swap a built-in of some sort, with a default implementation). I think I figured out the real reason though, it's more significant -- see my answer below.
@Mehrdad swap is built-in to the language. The problem is that without some knowledge of the internals of the class, any generic implementation of swap will have to do an assignment. Unless you can special case it based on knowing the actual semantics, you cannot implement it without using assignment.
2

Actually, after seeing juanchopanza's answer (which was deleted), I think I ended up figuring it out myself.

Copy-assignment operators allow classes like basic_string to avoid allocating resources unnecessarily when they can re-use them (in this case, memory).

So when you assign to a basic_string, an overloaded copy assignment operator would avoid allocating memory, and would just copy the string data directly to the existing buffer.

If the object had to be destroyed and constructed again, the buffer would have to be reallocated, which would be potentially much more costly for a small string.

(Note that vector could benefit from this too, but only if it knew that the elements' copy constructors would never throw exceptions. Otherwise it would need to maintain its exception safety and actually perform a copy-and-swap.)

Comments

1

It allows you to use assignment of other types as well.
You could have a class Person with an assignment operator that assigns an ID.

But besides that, you don't always want to copy all the members as they are.

The default assignment only does a shallow copy.
For example, if the class contains pointers, or locks, you dont always want to copy them from the other object.
Usually when you have pointers you want to use a deep copy, and maybe create a copy of the object that the pointers are pointed to.
And if you have locks, you want them to be specific to the object, and you don't want to copy their state from the other object.

It is actually a common practice to provide your own copy constructor and assignment operator if your class holds pointers as members.

1 Comment

With regards to your first sentence: having a standard defined copy assignment wouldn't prevent other user defined assignment operators.
0

I have used it often as a conversion constructor but with already existing objects. i.e assigning member variable type, etc to an object.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.