What is the operator= returning? - c++

Came across a code. I am posting the snippet.
Animal & Animal::operator=(const Animal & o) {
cout << "assignment operator" << endl;
if(this != &o) {
_type = o._type;
_name = clone_prefix + o._name;
_sound = o._sound;
}
return *this;
}
int main( int argc, char ** argv ) {
Animal a;
a.print();
const Animal b("goat", "bob", "baah");
b.print();
const Animal c = b;
c.print();
a = c;
a.print();
return 0;
My question: This is he pointer to the current object right? The assignment operator returns the object of Animal type right? since it is returning *this? But isn't it supposed to return address of object since its return type is Animal & ?
I am not understanding this.

The operator= returns an Animal&, i.e. a reference to an Animal. Maybe your confusion stems from the fact that & is also used to take the address of an object, i.e. to produce a pointer to an object. As a type modifier, it means "reference". So *this is correct.
Think about this code snippet:
Animal a{}; //suppose Animal has a default constructor
Animal& b = a;
Animal* c = &a;
Animal& d = *c;
a is the actual object, whereas b is a reference to a (not a pointer, so a goes on the right-hand side and not the address of a). c is a pointer to a, so we assign it the address of a with &a on the right-hand side. d is also a reference to a, but instead of just assigning a to it, we dereference the pointer c (completely equivalent to the second line). Imagine this instead of c and the return value of the function instead of d and you have return *this.
General note: It is a well-known idiom and a useful convention that operator= returns a reference of the assigned-to object, because it mimics the behavior of primitive types. However, chaining of assignments like
a = b = c
would also be possible if it returned by value (i.e. Animal instead of Animal&). However, this would create an extra copy (at least theoretically, if the compiler cannot optimize it away for some reason). No matter how you do it, returning by value or reference makes the above statement possible, whereas returning a pointer wouldn't. Users of your class would be really confused if the operator= returned a pointer to an Animal, i.e. an Animal*.

Related

Value cast vs Reference cast

What is the difference between value cast and reference cast? Why one of them invokes conversion (aka creating new object) and other doesn't? What are caveats of using casting on rhs?
Assume this is Derived . Why those will not actually cast to Base?
*this = (Base&) rhs
(Base)* this = rhs
Could you please show on simple examples?
Value cast creates a new value from an existing one; reference cast creates a new reference to the same existing value.
Reference cast neither changes the content of an existing object nor creates a new one; it is restricted to changing the interpretation of the value that is already there. Value casting, on the other hand, can make a new object from an existing one, so it has fewer restrictions.
For example, if you have an unsigned char and you want a value or a reference of type int, value cast is going to work, while reference casting is going to fail:
unsigned char orig = 'x';
int v(orig); // Works
int &r(orig); // Does not work
rhs is Derived, I want to assign all inherited and non-inherited stuff from rhs into Base
Then you need to cast both sides to Base, or add an assignment operator to Derived that takes a const Base& as an argument. Here is an example of casting on both sides (may be hard to understand by other programmers reading your code)
struct Base {
int x;
Base(int x) : x(x) {}
};
struct Derived1 : public Base {
Derived1(int x) : Base(x) {}
};
struct Derived2 : public Base {
Derived2(int x) : Base(x) {}
};
Running the code below
Derived1 d1(5);
Derived2 d2(10);
cout << d1.x << " " << d2.x << endl;
((Base&)d1) = (Base&)d2;
cout << d1.x << " " << d2.x << endl;
produces the following printout:
5 10
10 10
As you can see, the assignment ((Base&)d1) = (Base&)d2 copied the content of d2's Base portion into d1's Base portion (demo).
What is the difference between value cast and reference cast?
Value casts convert an object to the value:
char i = 'a';
int k = static_cast<int>(i); // Prefer C++ casts to C casts
Reference casts convert an object to a reference:
char i = 'a';
int &k = static_cast<int&>(i);
Just because the conversion can be done implicitly in int &k = i doesn't mean it doesn't happen.
Why one of them invokes conversion (aka creating new object) and other doesn't?
If you write int &x = static_cast<int&>(i), there are 2 things that can happen:
1) A pointer is created pointing to i (references are hidden pointers). Then this hidden pointer gets assigned to x, and x behaves as a reference of i.
2) Usually, the compiler optimizes away this reference, and simply considers x an alias of i. Therefore no variable is instantiated.
In the former case, a new object is created.
However, if you write:
char c = 'a';
int i = static_cast<int> (c);
there is no instantiation, just a copy of the memory from c to i.
Why those will not actually cast to Base?
*this = (Base&) rhs
You cannot assign the base object to a derived object, only the opposite. This will most likely overwrite the fields of the base object to the derived object's.
(Base)* this = rhs
There is no point in castling an l-value. This is equivalent to:
*this = rhs;
What are caveats of using casting on rhs?
I don't think there is anything wrong with casting, as long as they do not decrease readability.

operator overloading [operator A() const]

There is a pre-defined class named B as under:
class B
{
protected:
A ins;
public:
void print() {
cout<<"t";
}
operator A() const {
return ins;
}
};
Can anyone please explain the meaning of the line "operator A() const" and how can this be used to fetch "ins" in the main function?
This is a conversion operator that allows B objects to be converted to (cast to) A objects.
Let's break down operator A() const {...}
It is equivalent to something like A convert_to_A() { return ins; } except that by naming it operator A the compiler can use it automatically.
operator A means that this is an operator that converts to type A.
(): conversion operators are always function that take no parameters.
const because casting a B to an A must not change the value of the B. For example:
double d = 3.14;
int i = d;
Here d has been (implicity) cast to an int. i has the value 3, but d is still 3.14 -- the conversion did not change the original value.
In the context of your code we can say:
a B object contains a hidden A object
each B is willing to "pretend" to be an A ...
... by returning a copy of the A inside it
Allowing:
void f(A an_a) {...}
B my_b;
f(my_b);
Note that the conversion operator returns a copy of ins. Depending on context, you might want to change it to operator A&() const {...} to return a reference instead of a copy (if, for example, A was an expensive class to copy). However, this would allow the caller to change the A stored inside B, which is probably not want you want. To prevent the copy, but not allow changes, you would have to return a const reference to A. This is why you'll often see:
operator const A&() const { return ins;}
This mean you can cast your B instance to A and get the ins member of B.
B b;
A a;
a = (A)b;
// a is now equal to the member `ins` of b. Depending on the implementation of operator = of class A, it was probably copied into the a variable.

C++ referencing operator in declaration

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.

Dynamic binding in C++ on copied object

I have a problem in virtual function:
Here is some code as an example:
class A
{
public : virtual void print(void)
{
cout<< "A::print()"<<endl;
}
};
class B : public A
{
public : virtual void print(void)
{
cout<<"B::print()"<<endl;
}
};
class C : public A
{
public : void print(void)
{
cout<<"C::print()"<<endl;
}
};
int main(void)
{
A a,*pa,*pb,*pc;
B b;
C c;
pa=&a;
pb=&b;
pc=&c;
pa->print();
pb->print();
pc->print();
a=b;
a.print();
return 0;
}
the result:
A::print()
B::print()
C::print()
A::print()
I know it's a Polymorphism ,and know have a table called virtual-function-table,but I don't know how it is to achieve,And
a=b;
a.print();
the result is: A::print() not B::print(),why it hasn't polymorphism.
thank you!
The object a is still of type A. The assignment only copies data from b, it doesn't make a a B object.
This is called object slicing.
a=b;
a.print();
It will print A::print() because a=b causes object-slicing, which means a gets only the a-subobject of b. Read this :
What is object slicing? .
Note that runtime-polymorphism can be achieved only through pointer and reference types. In the above code, a is neither pointer, nor reference type:
A * ptr = &b; //syntax : * on LHS, & on RHS
A & ref = b; //syntax : & on LHS, that is it!
ptr->print(); //will call B::print() (which you've already seen)
ref.print(); //will call B::print() (which you've not seen yet)
Because a is not a pointer. It is an instance of A, and the assignment a=b; copies the instance of b to a. But the function call is on an instance of A.
When you do a = b; you b object is sliced i.e. only the A part of it gets copied. Polymorphism only works through pointers and references. Search "object slicing" to learn about the subject.
To learn more about virtual method table please see wiki. But in general case the table keeps the information methods' addresses. So, class A in the table will have one record says that method print is in address X. When you do pa=&b class B simply replace the table to its one in such a way that address of method print will point to address Y.
But when you do a=b you copy object. In this case polymorphism doesn't work.
After a call to any member function on a object of type A, you still have A object (except an explicit destructor call, which leaves nothing at all).
a = b;
The assignment of a class instance is just a call to a particular member function called "operator=". There is nothing special with "operator=" here, except its name is standard. You could use another name for assignment:
a = b;
// you could as well write:
a.assign(b);
// (if such member was defined)
Just as you could write add(a,b) instead of a+b, but a+b is more readable.
An explicit call to a function never changes the type of the variable it is called on:
A a;
foo(a);
// a is still a A
a.bar();
// a is still a A
The declared type of a is A, and cannot be changed to something else: this is an invariant of a.
This is also true for pointers: a variable of type pointer to A will always have the type pointer to A:
void foo (A*&);
A *bar();
A a;
A *p = &a;
foo (p); // might change p
// a still has type: pointer to A
p = bar();
// a still has type: pointer to A
But p might point to an object of type B, so, at runtime, the dynamic type of *p will be B; but the dynamic type of p is always A*.

C++ copy constructor causing code not to compile ( gcc )

I have the following code which doesn't compile. The compiler error is:
"error: no matching function to call B::B(B)",
candidates are B::B(B&) B::B(int)"
The code compiles under either of the following two conditions:
Uncommenting the function B(const B&)
change 'main' to the following
int main()
{
A a;
B b0;
B b1 = b0;
return 0;
}
If I do 1, the code compiles, but from the output it says it's calling the 'non const copy constructor'.
Can anyone tell me what's going on here?
using namespace std;
class B
{
public:
int k;
B()
{
cout<<"B()"<<endl;
}
B(int k)
{
cout<<"B(int)"<<endl;
this->k = k;
}
/*B(const B& rhs) {
cout<<"Copy constructor"<<endl;
k = rhs.k;
}*/
B(B& rhs)
{
cout<<"non const Copy constructor"<<endl;
k = rhs.k;
}
B operator=(B& rhs)
{
cout<<"assign operator"<<endl;
k = rhs.k;
return *this;
}
};
class A
{
public:
B get_a(void)
{
B* a = new B(10);
return *a;
}
};
int main()
{
A a;
B b0 = a.get_a(); // was a.just();
B b1 = b0 ;
return 0;
}
I've done some extra reading into this, and as I suspected all along, the reason why this occurs is due to return value optimization. As the Wikipedia article explains, RVO is the allowed mechanism by which compilers are allowed to eliminate temporary objects in the process of assigning them or copying them into permanent variables. Additionally, RVO is one of the few features (if not the only) which are allowed to violate the as-if rule, whereby compilers are only allowed to make optimizations only if they have the same observable behaviours as if the optimization were never made in the first place -- an exemption which is key in explaining the behaviour here (I admittedly only learned of that exemption as I was researching this question, which is why I was also confused initially).
In your case, GCC is smart enough to avoid one of the two copies. To boil your code down to a simpler example
B returnB()
{
B a;
B* b = &a;
return *b;
}
int main()
{
B c = returnB();
return 0;
}
If one follows the standard and does not perform RVO, two copies are made in the process of making c -- the copy of *b into returnB's return value, and the copy of the return value into c itself. In your case, GCC omits the first copy and instead makes only one copy, from *b directly into c. That also explains why B(B&) is called instead of B(const B&) -- since *b (a.k.a. a) is not a temporary value, the compiler doesn't need to use B(const B&) anymore and instead chooses the simpler B(B&) call instead when constructing c (a non-const overload is always automatically preferred over a const overload if the choice exists).
So why does the compiler still give an error if B(const B&) isn't there? That's because your code's syntax must be correct before optimizations (like RVO) can be made. In the above example, returnB() is returning a temporary (according to the C++ syntax rules), so the compiler must see a B(const B&) copy constructor. However, once your code is confirmed to be grammatically correct by the compiler, it then can make the optimization such that B(const B&) is never used anyway.
EDIT: Hat tip to Charles Bailey who found the following in the C++ standard
12.2 [class.temporary]: "Even when the creation of the temporary is avoided,
all the semantic restrictions must be
respected as if the temporary object
was created."
which just reinforces and confirms the need for a copy constructor taking a reference to const when temporaries are to be copied for construction (irrespective of whether or not the constructor is actually used)
Your get_a() function returns an object B, not a reference (but leaks the newly-created B object). Also, for assignments to work as you're doing, you need to make sure your assignment operator and copy constructors are both taking const B& arguments — then B b1 = b0 will work in this case. This works:
class B
{
public:
int k;
B() { cout<<"B()"<<endl; }
B(int k) {
cout<<"B(int)"<<endl;
this->k = k;
}
B(const B& rhs) {
cout<<"non const Copy constructor"<<endl;
k = rhs.k;
}
B operator=(const B& rhs) {
cout<<"assign operator"<<endl;
k = rhs.k;
return *this;
}
};
class A {
public:
B* get_a(void) {
B* a = new B(10);
return a;
}
B get_a2(void) {
B a(10);
return a;
}
};
int main() {
A a;
B b0 = *a.get_a(); // bad: result from get_a() never freed!
B b1 = a.get_a2(); // this works too
return 0;
}
Short explanation: functions that return by value create a temporary object which is treated as constant and therefore cannot be passed to functions accepting by reference, it can only be passed to functions accepting const reference. If you really allocate an object in get_a(), you should really be returning a pointer (so that you remember to delete it, hopefully) or in the worst case - a reference. If you really want to return a copy - create the object on the stack.
Long explanation: To understand why your code doesn't compile if there is only "non-const copy constructor"1, you need to be familiar with the terms lvalue and rvalue. They originally meant that rvalues can only stand on the right side of operator = (assignment) while lvalues can stand also on the left side. Example:
T a, b;
const T ac;
a = b; // a can appear on the left of =
b = a; // so can b => a and b are lvalues in this context
ac = a; // however, ac is const so assignment doesn't make sense<sup>2</sup>, ac is a rvalue
When the compiler is performing overload resolution (finding which overload of a function/method best match the provided arguments) it will allow lvalues to match parameters passed by value3, reference and const reference types. However, it will match rvalues only against value3 and const reference parameters. And that's because in some sense, since rvalues cannot be put on the left side of operator =, they have read-only semantic, and when it shouldn't be allowed to modify them. And when you accept a parameter through non-const reference, it's implied that you'll somehow change this parameter.
The last piece of the puzzle: temporary objects are rvalues. Function returning by value creates a temporary object with very limited lifespan. Because of its limited lifespan it's considered const, and is therefore a rvalue. And this rvalue doesn't match functions with parameters by non-const reference. Examples:
void f_cref(const A& a) { std::cout << "const ref" << std::endl; }
void f_ref(A& a) { std::cout << "non-const ref" << std::endl; }
A geta() { return A(); }
A a;
const A ac;
f_ref(a); // ok, a is a lvalue
f_ref(ac); // error, passing const to non-const - rvalue as lvalue - it's easy to spot here
f_cref(a); // ok, you can always pass non-const to const (lvalues to rvalues)
f_ref(geta()); // error, passing temporary and therefore const object as reference
f_cref(geta()); // ok, temporary as const reference
Now you have all the information to figure out why your code doesn't compile. Copy constructor are like regular functions.
I have oversimplified things a bit, so better, more complete and correct explanation can be found at this excellent Visual C++ Studio Team blog post about rvalue references, which also addresses the new C++ 0x feature "rvalue references"
1 - there's no such thing as non-const copy constructor. The copy constructor accepts const reference, period.
2 - you can probably put const object on the left of = if it has its operator = declared const. But that would be terrible, terrible, nonsensical thing to do.
3 - actually, you wouldn't be able to pass const A by value if A doesn't have a copy constructor - one that accepts const A& that is.
The line B b1 = b0; is the culprit. This line requires calling a copy constructor. You could fix the code by writing B b1(b0) instead, or by defining a copy constructor, which takes a const B&, but not a B&.