I am a beginner in C++, and this must be a really basic question. I understand & stands for referencing operation. For example, a = &b assigns the address of b to a. However, what does & in a declaration such as the following mean?
className& operator=(const className&);
Do the following make sense and what is the difference between the last line and the following?
className* operator=(const className*);
From the answers below, it seems --- as I understand it --- that the following is valid too. Is it? If it is, how is it different from the version with "&"?
className operator=(const className);
After reading the following answers and some more outside, I realized part of my original confusion stems from mixing up reference as in general computer science and reference type as in C++. Thank you all who answered my question. All the answers clarify my understanding to different degrees, even though I can only pick one as the accepted answer.
The token & has three distinct meanings in C++, two of which are inherited from C, and one of which is not.
It's the bitwise AND operator (unless overloaded).
It's the address operator, which acts on an lvalue to yield a pointer to it. (Unless overloaded.) This is what is happening in a = &b.
It denotes a reference type. const className& as a parameter is a const reference to an object of type className, so when the function is called, the argument will be passed by reference, but it won't be allowed to be modified by the function. The function you gave also returns a reference.
Assignment Operator
Understanding is best gained by example:
class A {
int x;
public:
A(int value) : x(value) {}
A& operator=(const A& from) { // Edit: added missing '=' in 'operator='
x = from.x;
return *this;
}
};
A m1(7);
A m2(9);
m2 = m1; /// <--- calls the function above, replaces m2.x with 7
Here, we defined the assignment operator. This special method is designed to provide assignment capability to objects.
The reason that it returns a reference to *this is so you can chain assignments without excessive memory copies:
A m3(11);
m3 = m1 = m2; /// <--- now, m3.x and m1.x both set to m2.x
Expanded as follows:
m3 = ( something )
where
(something) is a reference to the object m1
by result of call to m1.operator=(m2) method
such that
the returned reference is then passed into m3.operator(...)
Chaining lets you do this:
(m1=m2).function(); // calls m1.function after calling assignment operator
Libraries such as boost leverage this pattern (for a different type of chaining example, see the program options library in boost) and it can be useful when developing a domain specific 'language'.
If instead full objects were returned from operator=, the chained assignment would at a minimum involve multiple extra copies of the objects, wasting CPU cycles and memory. In many cases things would not work properly because of the copy.
Essentially, using a reference avoids a copy.
Note
In reality, (simplified explanation) a reference is just a fancy syntax for a C pointer.
In the common case, you can then write code with A.x instead of A->x.
Caution
Returning a pure reference from a method is often dangerous; newcomers can be tempted to return a reference to an object constructed locally inside the method on the stack, which depending on the object can lead to obscure bugs.
Your pointer example
It depends on what you return from the body of the method, but regardless, the following would instead return a pointer to some instance of className:
className* operator=(const className*);
This will compile and it even seems to work (if you return this from the method), but this does violate the Rule of Least Surprise, as it is likely anyone else attempting to use your code would not expect the assignment operator to return a pointer.
If you think about base types:
int x=1; int y=2; int z; z=y=x;
will never ever do anything other than return integers - so having operator= return the assigned to object is consistent)
It also doesn't let you do this:
(m1 = m2).something
It also allows you to pass NULL which is something assignment operators don't typically want to care about.
Instead of writing
blah& operator(const blah& x) { a = x.a ; return *this; }
You would need to write:
blah* operator(const blah* x) {
if (x) { a = x->a ; return this; }
else { /*handle null pointer*/
}
It means the function takes a reference to a const className and returns a reference to a className
Say you have a class like below:
class className
{
public:
className& operator=(const className& rhs)
{
this->a_ = rhs.a_;
this->b_ = rhs.b_;
return *this;
}
private:
std::string a_;
std::string b_;
};
You can use the assignment operator of the class as below:
className a;
className b;
className c;
c = b = a;
The above line is executed as:
c.operator=(b.operator=(a));
Now take another class that has the operator= defined using the second form:
class className2
{
public:
className2* operator=(const className2* rhs)
{
this->a_ = rhs->a_;
this->b_ = rhs->b_;
return this;
}
private:
std::string a_;
std::string b_;
};
You can use the assignment operator of the class as below:
className2 obj1;
className2 obj2;
className2 obj3;
obj2 = &obj1;
obj3 = &obj2;
The above lines are executed as:
obj2.operator=(&obj1);
obj3.operator=(&obj2);
However, such coding is not intuitive. You don't assign a pointer to an object. It is more intuitive to say:
className obj1;
className* objPtr = NULL;
objPtr = &obj1;
a = &b
Here, & gives address of b.
className& operator=(const className&);
Here, operator = will take const reference of variable of type className.
You can say in C++, & is polymorphic.
Related
I'm doing some revision of my C++, and I'm dealing with operator overloading at the minute, specifically the "="(assignment) operator. I was looking online and came across multiple topics discussing it. In my own notes, I have all my examples taken down as something like
class Foo
{
public:
int x;
int y;
void operator=(const Foo&);
};
void Foo::operator=(const Foo &rhs)
{
x = rhs.x;
y = rhs.y;
}
In all the references I found online, I noticed that the operator returns a reference to the source object.
Why is the correct way to return a reference to the object as opposed to the nothing at all?
The usual form returns a reference to the target object to allow assignment chaining. Otherwise, it wouldn't be possible to do:
Foo a, b, c;
// ...
a = b = c;
Still, keep in mind that getting right the assigment operator is tougher than it might seem.
The return type doesn't matter when you're just performing a single assignment in a statement like this:
x = y;
It starts to matter when you do this:
if ((x = y)) {
... and really matters when you do this:
x = y = z;
That's why you return the current object: to allow chaining assignments with the correct associativity. It's a good general practice.
Your assignment operator should always do these three things:
Take a const-reference input (const MyClass &rhs) as the right hand side of the assignment. The reason for this should be obvious, since we don't want to accidentally change that value; we only want to change what's on the left hand side.
Always return a reference to the newly altered left hand side, return *this. This is to allow operator chaining, e.g. a = b = c;.
Always check for self assignment (this == &rhs). This is especially important when your class does its own memory allocation.
MyClass& MyClass::operator=(const MyClass &rhs) {
// Check for self-assignment!
if (this == &rhs) // Same object?
return *this; // Yes, so skip assignment, and just return *this.
... // Deallocate, allocate new space, copy values, etc...
return *this; //Return self
}
When you overload an operator and use it, what really happens at compilation is this:
Foo a, b, c;
a = b;
//Compiler implicitly converts this call into the following function call:
a.operator=(b);
So you can see that the object b of type FOO is passed by value as argument to the object a's assignment function of the same type. Now consider this, what if you wanted to cascade assignment and do something like this:
a = b = c;
//This is what the compiler does to this statement:
a.operator=(b.operator=(c));
It would be efficient to pass the objects by reference as argument to the function call because we know that NOT doing that we pass by value which makes a copy inside a function of the object which takes time and space.
The statement 'b.operator=(c)' will execute first in this statement and it will return a reference to the object had we overloaded the operator to return a reference to the current object:
Foo &operator=(const Foo& rhs);
Now our statement:
a.operator=(b.operator=(c));
becomes:
a.operator(Foo &this);
Where 'this' is the reference to the object that was returned after the execution of 'b.operator=(c)'. Object's reference is being passed here as the argument and the compiler doesn't have to create a copy of the object that was returned.
Had we not made the function to return Foo object or its reference and had made it return void instead:
void operator=(const Foo& rhs);
The statement would've become something like:
a.operator=(void);
And this would've thrown compilation error.
TL;DR You return the object or the reference to the object to cascade(chain) assignment which is:
a = b = c;
So for example:
const Ball& operator=(const Ball& other)
{
// Irrelevant code
return *this;
}
So I have heard that you return a reference to an object because of cascading:
Ball a,b,c;
// Unimportant initializations
a=b=c;
But why can't the method be something like:
const Ball operator=(const Ball& other)
{
// Irrelevant code
return *this;
}
Or this:
const Ball* operator=(const Ball& other)
{
// Irrelevant code
return this;
}
What if I do something like:
Ball *a, *b, *c;
// Unimportant initializations
a = b;
Would this work with both the methods I provided?
I apologize if these are dumb questions. I am still fairly new at C++.
Any help would be nice.
The return type is convention, but normally it's Ball& not const Ball&. I think you can have anything there you like (void for instance, if you don't want to return anything at all). But it's best to have something that behaves like the assignment operator does with built in types. That way people won't be surprised how your assignment operator behaves.
This code
Ball *a, *b, *c;
// Unimportant initializations
a = b;
is just pointer assignment, it will always be legal but it has nothing to do with any assignment operator you define which is for Ball objects not Ball pointers.
Returning by value would work, but you make unnecessary copies.
Returning by pointer would prevent natural chaining, you'd need some weird thing like
a = *(b = c);
And
Ball *pa;
Ball *pb;
pa = pb;
will not use your user-defined assignment operator, the assignment operator for pointers is built-in and does pointer assignment.
If you have a const reference then you cannot assign to it. If you return a pointer, again, you assign the pointer value, not the object instance.
In most cases it makes sense to stick to what is called "int semantics" whereas you make your classes behave in the same manner int does. In particular, you want cascading to work.
If you have a reference, you can chain your operations, like this
a.add(b).subtract(c);
But if you return a pointer or const, you can not do that.
You can define it this way:
const Ball operator=(const Ball& other)
{
// Irrelevant code
return *this;
}
However, you will be returning a temporary copy. The compiler will likely fix this for you when it optimizes. Additionally, chaining a = b = c when operator= takes a const Ball&, you are passing a reference to a temporary.
Returning a pointer will not work as you cannot chain, and you are dealing with different data types (pointers on the left hand side, const references on the right).
This snippet is legal:
int a;
(a = 2)++;
In one statement, we assign 2 to a and then increment a. This means that a = 2 must evaluate to a reference to a.
In order to replicate this behavior with user made types, the assignment operator needs to return a reference.
I'm doing some revision of my C++, and I'm dealing with operator overloading at the minute, specifically the "="(assignment) operator. I was looking online and came across multiple topics discussing it. In my own notes, I have all my examples taken down as something like
class Foo
{
public:
int x;
int y;
void operator=(const Foo&);
};
void Foo::operator=(const Foo &rhs)
{
x = rhs.x;
y = rhs.y;
}
In all the references I found online, I noticed that the operator returns a reference to the source object.
Why is the correct way to return a reference to the object as opposed to the nothing at all?
The usual form returns a reference to the target object to allow assignment chaining. Otherwise, it wouldn't be possible to do:
Foo a, b, c;
// ...
a = b = c;
Still, keep in mind that getting right the assigment operator is tougher than it might seem.
The return type doesn't matter when you're just performing a single assignment in a statement like this:
x = y;
It starts to matter when you do this:
if ((x = y)) {
... and really matters when you do this:
x = y = z;
That's why you return the current object: to allow chaining assignments with the correct associativity. It's a good general practice.
Your assignment operator should always do these three things:
Take a const-reference input (const MyClass &rhs) as the right hand side of the assignment. The reason for this should be obvious, since we don't want to accidentally change that value; we only want to change what's on the left hand side.
Always return a reference to the newly altered left hand side, return *this. This is to allow operator chaining, e.g. a = b = c;.
Always check for self assignment (this == &rhs). This is especially important when your class does its own memory allocation.
MyClass& MyClass::operator=(const MyClass &rhs) {
// Check for self-assignment!
if (this == &rhs) // Same object?
return *this; // Yes, so skip assignment, and just return *this.
... // Deallocate, allocate new space, copy values, etc...
return *this; //Return self
}
When you overload an operator and use it, what really happens at compilation is this:
Foo a, b, c;
a = b;
//Compiler implicitly converts this call into the following function call:
a.operator=(b);
So you can see that the object b of type FOO is passed by value as argument to the object a's assignment function of the same type. Now consider this, what if you wanted to cascade assignment and do something like this:
a = b = c;
//This is what the compiler does to this statement:
a.operator=(b.operator=(c));
It would be efficient to pass the objects by reference as argument to the function call because we know that NOT doing that we pass by value which makes a copy inside a function of the object which takes time and space.
The statement 'b.operator=(c)' will execute first in this statement and it will return a reference to the object had we overloaded the operator to return a reference to the current object:
Foo &operator=(const Foo& rhs);
Now our statement:
a.operator=(b.operator=(c));
becomes:
a.operator(Foo &this);
Where 'this' is the reference to the object that was returned after the execution of 'b.operator=(c)'. Object's reference is being passed here as the argument and the compiler doesn't have to create a copy of the object that was returned.
Had we not made the function to return Foo object or its reference and had made it return void instead:
void operator=(const Foo& rhs);
The statement would've become something like:
a.operator=(void);
And this would've thrown compilation error.
TL;DR You return the object or the reference to the object to cascade(chain) assignment which is:
a = b = c;
class item {
public:
item& operator=(const item &rh) {
...
...
return *this;
}
};
Is the following signature wrong?
void operator=(const item &rh);
item a, b;
a = b; // equivalent to a.operator=(b); so there is no need to return this.
It is not "wrong", but surprising. An assignment evaluates to the target object. That's what the builtin meaning is. If you define it different for your own class, people could become confused.
Example:
int c;
while((c = getchar()) != EOF) {
// ...
}
The assignment to c returned c itself and compared it to EOF afterwards. Users would expect your item class to behave similar.
The signature with void would not allow chained assignments:
a = b = c;
(Look at Johannes' answer for one more example of a usage pattern based on assignment returning the assigned value.)
That's why using such signatures is discouraged. However, if I am not mistaken, you actually can use such a signature.
It's perfectly legal. But when you declare operator= like that, you will be unable to make "chain of assignment":
item a(X);
item b;
item c;
c = b = a;
Reference allows modifying returned value. Since operator= is evaluated from right to left, the usage I showed to you is working.
EDIT Also, as others mentioned, return value is often used in expressions like while (a = cin.get()) != 'q'). But you also can declare operator like A operator=(const A&) (returns copy) or const A& operator(const A&) (returns const reference). My point is: this operator can return anything, but the idiomatic way is to return non-const reference to itself.
if I wanted to return "this" from a class member function as reference would this piece of code be correct ?
Record& operator=(const Record& that) {
m_name = that.m_name;
return *this;
}
Shouldn't i just use "return this" ?
Thanks for help :)
Yup, that's correct.
Return this wouldn't work since this is a pointer. (The reason it's a pointer and not a reference is because references weren't introduced into the language until after classes.)
In this specific case, if you're just going to assign the members anyway you shouldn't write a copy assignment operator; the default will do the same. When you manage some resource (and invoke the Rule of Three), you'd want to use the copy-and-swap idiom.
Your code is correct, but your comment shouldn't I just use "return this" is wrong (or, more accurately, the answer is "no, you shouldn't, and if you try, any compiler that works even halfway correctly compiler will stop you and give an error message.")
Given a class like:
class T {
void X() {}
void Y() const {}
};
In T::X, this will have the type T *const, and in T::Y, this will have the type T const *const. Either way, it's a pointer to T, not a reference to T. To get a reference to T (for returning or any other purpose) you need to use *this.
It may not matter for this specific case, but it is a good practice to have self assignment guard in the copy-assignment operator.
Record& operator=(const Record& that) {
if( &that != this ) {
m_name = that.m_name;
}
return *this;
}
This guards against statements like
Record r;
// do something with r
r = r; // this is protected
It would be important when the class is doing some resource (e.g. dynamically allocated memory) management.