C++ overriding the assignment operator - c++

To understand constructor and assignment, I wrote a very simply testing code like this:
class A {
public:
A() { std::cout<<"This is default cstr."; }
A(int i) { std::cout<<"This is int cstr. value is "<<i; }
A(const A &a) { std::cout<<"This is copy cstr."; }
A operator=(const A &a) { std::cout<<"This is assignment operator."; return *this;// this line is tricky }
};
int _tmain(int argc, _TCHAR* argv[]) {
std::cout<<"line 1 "; A a1; std::cout<<std::endl;
std::cout<<"line 2 "; A a2 = A(1); std::cout<<std::endl;
std::cout<<"line 3 "; a1 = a2; std::cout<<std::endl;
return 0;
}
For line 3 I got:
line 3 This is assignment operator.This is copy cstr.
But if I change return *this; to return NULL, I got:
line 3 This is assignment operator.This is int cstr. value is 0
Could someone explain what happened inside for me?

Your operator is returning A instead of A&:
A operator=(const A &a)
So when you return NULL, you are calling the implicit constructor A(int) and passing NULL to it.

The problem
line 3 This is assignment operator.This is copy cstr.
Your code's calling:
A operator=(const A &a) { std::cout<<"This is assignment operator."; return *this;
This obviously prints "This is assignment operator.", then return *this; statement sees the return type of A and creates a return value of type A doing the equivalent of A(*this); -> that calls the copy constructor, explaining this part of the output:
line 3 This is assignment operator.This is copy cstr.
^^^^^^^^^^^^^^^^^^
But if I change return *this; to return NULL, I got:
line 3 This is assignment operator.This is int cstr. value is 0
In this case:
A operator=(const A &a) { std::cout<<"This is assignment operator."; return NULL; }
You end up creating the return value of type A as per A(NULL), and as NULL is 0, that matches the A(int) constructor best, which is why you see:
line 3 This is assignment operator.This is int cstr. value is 0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The solution
A& operator=(const A &a) { std::cout<<"This is assignment operator."; return *this; }
^
You normally want the assignment operator to return a reference to the *this object. That way, no additional A object is constructed as the assignment operator function returns.
Aside - why return `A&` anyway?
The reason A& is returned and not void, is that it allows further chained use of the object, as in:
a1 = a2 = a3;
Which is evaluated as:
a1.operator=(a2.operator=(a3));
Is a2.operator= returned void then there'd be no usable argument to a1.operator=().
A non-const reference supports usage like:
make_uppercase(my_string = other_string);
In some other languages, that would need to be broken into two statements. Whether you wish it was depends on whether you find it confusing, and how much you value concision.

Your code says
A operator = (const A& a)
You take a reference to an A, you modify yourself, and then you return A(*this) which invokes the copy-constructor to create a new instance and return by value.
What you probably intended was
A& operator = (const A& a)
This will then return a reference to *this rather needing to copy it into a new temporary instance.
Be aware that NULL is a macro alias for '0UL' or '0ULL' which the compiler detects as being a match for A(int). This is one of the reasons that C++11 introduced nullptr as an alternative to NULL.

Related

Copy constructor is called instead of move constructor - why?

I have this code, taken from here by the way http://www.cplusplus.com/doc/tutorial/classes2/
// move constructor/assignment
#include <iostream>
#include <string>
#include <utility>
using namespace std;
class Example6
{
string* ptr;
public:
Example6(const string& str) :
ptr(new string(str))
{
cout << "DONT MOVE " << '\n';
}
~Example6()
{
delete ptr;
}
// move constructor
Example6(Example6&& x) :
ptr(x.ptr)
{
cout << "MOVE " << '\n';
x.ptr = nullptr;
}
// move assignment
Example6& operator=(Example6&& x)
{
delete ptr;
ptr = x.ptr;
x.ptr = nullptr;
return *this;
}
// access content:
const string& content() const
{
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs)
{
return Example6(content() + rhs.content());
}
};
int main()
{
Example6 foo("Exam");
Example6 bar = Example6("ple"); // move-construction
foo = foo + bar; // move-assignment
cout << "foo's content: " << foo.content() << '\n';
return 0;
}
I only added output in constructor to see which is being called. To my surprise it is always the first one, copy constructor. Why does it happen? I did some research and found some info about elision. Is it somehow possible to prevent it and always call move constructor?
Also, as a side note, as I said this code is from cplusplus.com. However, I read about move semantics in some other places and I wonder if this move constructor here is done right. Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move, because x is rvalue reference that has a name, so it is really lvalue and we need to use move to cast it to be rvalue. Do i miss something, or is it really tutorial's mistake?
Btw, adding move doesn't solve my first problem.
So anything with a name is an lvalue.
An rvalue reference with a name is an lvalue.
An rvalue reference will bind to rvalues, but it itself is an lvalue.
So x in ptr(x.ptr) is an rvalue reference, but it has a name, so it is an lvalue.
To treat it as an rvalue, you need to do ptr( std::move(x).ptr ).
Of course, this is mostly useless, as moving a ptr does nothing as ptr is a dumb raw pointer.
You should be following the rule of 0 here.
class Example6 {
std::unique_ptr<string> ptr;
public:
Example6 (string str) : ptr(std::make_unique<string>(std::move(str))) {cout << "DONT MOVE " << '\n';}
Example6():Example6("") {}
~Example6 () = default;
// move constructor
Example6 (Example6&& x) = default;
// move assignment
Example6& operator= (Example6&& x) = default;
// access content:
const string& content() const {
if (!ptr) *this=Example6{};
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs) {
return Example6(content()+rhs.content());
}
};
because business logic and lifetime management don't belong intermixed in the same class.
While we are at it:
// addition:
Example6& operator+=(const Example6& rhs) & {
if (!ptr) *this = Example6{};
*ptr += rhs.content();
return *this;
}
// addition:
friend Example6 operator+(Example6 lhs, const Example6& rhs) {
lhs += rhs;
return lhs;
}
Copy constructor is called ... - why?
The premise of your question is faulty: The copy constructor is not called. In fact, the class is not copyable.
The first constructor is a converting constructor from std::string. The converting constructor is called because Example6 objects are initialised with a string argument. Once in each of these expressions:
Example6 foo("Exam")
Example6("ple")
Example6(content() + rhs.content()
... instead of move constructor
There are a few copy-initialisations by move in the program. However, all of them can be elided by the compiler.
Is it somehow possible to prevent it and always call move constructor?
There are a few mistakes that can prevent copy elision. For example, if you wrote the addition operator like this:
return std::move(Example6(content()+rhs.content()));
The compiler would fail to elide the move and probably tell you about it if you're lucky:
warning: moving a temporary object prevents copy elision
Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
There's no need. Moving a pointer is exactly the same as copying a pointer. Same holds for all fundamental types.
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move
ptr is not a string. It is a pointer to a string. Copying a pointer does nothing to the pointed object.
PS. The example program is quite bad quality. There should never be owning bare pointers in C++.
I can say your class does not have a copy constructor.
Because copy ctor parameter have to be const and reference
class Example6{
public:
Example6(const Example6 &r);
};

What is the difference between rvalue reference and lvalue reference?

After reading some materiales about rvalue reference i have more question then answers. From here i have read about rvalue ref:
Doc rvalue ref (1)
Doc rvalue ref (2)
Doc rvalue ref (3 - book)
Here i made a simple example to help me understand:
#include <iostream>
using namespace std;
class A
{
public:
A() :m_a(0), m_pa(nullptr) { cout << "constructor call" << endl; };
~A() { cout << "destructor call" << endl; };
A(A& other) :m_a(0), m_pa(nullptr)
{
cout << "copy constructor" << endl;
}
A(A&& other) :m_a(0), m_pa(nullptr)
{
cout << "move constructor" << endl;
}
A& operator=(A&& other)
{
this->m_a = other.m_a;
this->m_pa = other.m_pa;
other.m_a = 0;
other.m_pa = nullptr;
return *this;
}
A& operator=(A& other)
{
this->m_a = other.m_a;
this->m_pa = other.m_pa;
other.m_a = 0;
other.m_pa = nullptr;
return *this;
}
private:
int m_a;
int* m_pa;
};
int main()
{
A(test2);//constructor
A test4(test2);//copy constructor
//? - move constructor
return 0;
}
I don't understand what is so special with &&. In the above example i can do something like this with &.
A& operator=(A& other)
{
this->m_a = other.m_a; //copy value
this->m_pa = other.m_pa;//copy pointer address
other.m_a = 0;
other.m_pa = nullptr;//clean "other" object properties from preventing destructor to delete them and lose pointer address
return *this;
}
Question:
If i can do this with & without using extra memory allocation and copy operation why should i use &&?
How is a value value taken that has no identifier and saved?
Example 2:
#include <iostream>
using namespace std;
void printReference (int& value)
{
cout << "lvalue: value = " << value << endl;
}
void printReference (int&& value)
{
cout << "rvalue: value = " << value << endl;
}
int getValue ()
{
int temp_ii = 99;
return temp_ii;
}
int main()
{
int ii = 11;
printReference(ii);
printReference(getValue()); // printReference(99);
return 0;
}
Question:
Why to use && in this case and how does this help me? Why not just store the return of getValue and print it?
After you read some stuff about rvalues, here is some more material about rvalues.
I think the point you are probably missing, is not (only) what you can do but what you should do.
Your first example has several issues:
Your are not able to copy a const value to an instance of A.
const A a1;
A a2(a1); // won't compile
A a3;
a3 = a1; // won't compile
I don't understand what is so special with &&. In the above example i can do something like this with &.
Yes you could do what you suggested. But it is purely designed copy assigment. Consider this:
I wrote a shared library where my copy ctor is like you did in your suggestion. You don't have access to my code, just the header. In my copy ctor and assigment operator i take ownership of the instance you passed to my library. There is no description what the assignment is doing... Do you see the point, I must not take ownership of your instances! eg:
// my header:
// copy ctor
A& operator=(A& other);
// your code:
A a1;
A a2(a1); // from now on, a1 is broken and you don't know it!!!
cout << a1; // pseudo code: prints garbage, UD, crash!!!
You always should define copy-ctors/assignments parameters const:
A(A const& other);
A& operator=(A const& other);
// ...
const A a1;
A a2(a1); // will compile
A a3;
a3 = a1; // will compile + due to the fact a1 is passed const a3 cannot mutate a1 (does not take ownership)
cout << a1; // pseudo code: everything is fine
a3 = std::move(a1); // a3 takes ownership from a1 but you stated this explicitly as you want it
Here is a small example you can play with. Notice the copy constructor is const but the copy assignment is not. Then you can see how they differ.
If i can do this with & without using extra memory allocation and copy operation why should i use &&?
The assignment operators you wrote taking & lvalue references are very, very bad. You don't expect statements like
a = b;
to damage b, but that's what you're suggesting. The usual assignment operator takes a const& precisely because it shouldn't alter the right-hand-side of the expression.
So, you should use the rvalue-reference assignment operator (move assignment) when you do want to steal the right-hand-side's state, such as when it's an anonymous temporary or you explicitly move it:
a = return_anonymous_temporary(); // ok: the rhs value would expire anyway
a = std::move(b); // ok: the damage to b is explicit now
That behaviour shouldn't be the default, though.
If i can do this with & without using extra memory allocation and copy operation why should i use &&?
Because it allows you to overload a function for rvalues and lvalues and have different behaviour for each. You have same behaviour in both overloads, so you don't need an rvalue reference in this case.
More generally, an rvalue reference argument allows you to pass a temporary, while allowing a move from that argument.
Why to use && in this case and how does this help me?
It allowed you to print "rvalue" when the argument was an rvalue.
Why not just store the return of getValue and print it?
Then you won't be able to print "rvalue" for rvalues and "lvalue" for lvalues.
Except for move constructor/assignment operator, there are not many cases where r-values are useful.

Implicity constructor call

SLet's take this class:
class standardClass
{
public:
standardClass(int) {}
~standardClass() {}
standardClass(standardClass&) {}
standardClass & operator=(standardClass&)
{
return *this;
}
};
int main()
{
standardClass stdClassObj1(1);
standardClass stdClassObj2(stdClassObj1);
standardClass stdClassObj3 = stdClassObj2;
stdClassObj1 = stdClassObj2;
stdClassObj2 = standardClass(4);
stdClassObj2 = 4;
}
I am getting an error on the last assignment.
All these lines are allowed except the last one. In the last one the integer 4 does not invoke the constructor. The error says there is no assignment operator for this. I understand that. But what I'm confused is why this implicit conversion works for a new object (line 1) and not to an existing object (line 2).
I do understand that in the case of line 1, copy constructor is called to create the new AObj4. But in line 2 it invokes the assingment operator. Why cant it make a temporary object with integer 4 and invoke the assignment operator as it would do for line 3?
standardClass(standardClass &objToCopy) // copy constructor
A copy constructor should take a const-qualified reference, like so:
standardClass(const standardClass &objToCopy)
Same for your assignment operator: that should be
standardClass & operator=(const standardClass &objToCopy)
Non-const-qualified lvalue references cannot be used with temporary objects, such as the temporary object that would otherwise be created from your literal 4.
You just changed the entire class. The problem is that you have:
standardClass & operator=(standardClass &objToCopy) // assignment operator
{
cout << "Copy assignment operator: " << objID << endl;
objID = objToCopy.objID;
return *this;
}
4 cannot be converted to a standardClass &.
If you use:
standardClass & operator=(standardClass const& objToCopy)
// ^^^^^^
{
cout << "Copy assignment operator: " << objID << endl;
objID = objToCopy.objID;
return *this;
}
all will be ok.

Why is copy constructor not called if the overloaded + and = operators return by value in the following case?

When [abc e = a+b] is called, the copy constructor is not called.
class abc{
int i;
public:
abc()
{
i = 10;
cout<<"constructor"<<endl;
}
abc(const abc &a)
{
cout<<"copy constructor"<<endl;
i = a.i;
}
abc operator=(const abc &a)
{
cout<<"operator="<<endl;
abc temp;
temp.i = a.i;
return temp;
}
abc operator+(abc& a)
{
cout <<"Operator+ called"<<endl;
abc temp;
temp.i = i+a.i;
return temp ;
}
};
int main()
{
abc a,b;
cout <<"----------------------------------------------"<<endl;
a = b;
cout <<"----------------------------------------------"<<endl;
abc c = a;
cout <<"-----------------------------------------------"<<endl;
abc d(a);
cout <<"-------------------------------------------"<<endl;
**abc e = a+b;**
}
However if the overload operators methods are replaced with the following methods that return references to the object of class abc, copy constructor gets called.
abc& operator=(const abc &a)
{
cout<<"operator="<<endl;
i = a.i;
return *this;
}
abc& operator+(const abc& a)
{
cout <<"Operator+ called"<<endl;
i = i+a.i;
return *this ;
}
Can some one please explain why does this happen?
This is happening due to return value optimization.
Since no costly memory-copying operations are being made if your constructor returns a reference, it works as you would expect, because no optimization is being made in this case. If it returns a value, the optimization kicks in and it works in a somewhat unexpected (for you) manner, but it is allowed by the standard.
Its an optimization explicitly allowed by the standard.
In n3376
12.8 paragraph 31:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.
This optimization can be achieved via RVO (this case) and under certain conditions NRVO.
You can disable optimization options on your compiler. Please check for the optimization flag used while compiling your code.

why compiler is not eliding away copy construction in this case

class test
{
public:
int data;
test(int var = 0):data(var){cout<<"constructor"<<endl;}
~test(){ cout<<"destructor"<<endl; }
test(const test& var)
{
cout<<"copy constructor"<<endl;
this->data = var.data;
}
test& operator=( const test& var)
{
cout<<"assignment op"<<endl;
this->data = var.data;
return *this;
}
};
test passByref_returnByVal(test& obj)
{
return obj;
}
int main()
{
test o1(5);
test o2 = passByref_returnByVal(o1);
cout<<"=========================="<<endl;
test o3;
o3 = passByref_returnByVal(o1);
}
Output:
constructor
copy constructor
constructor
copy constructor
assignment op
destructor
In the given example , object o2 is directly copy constructed without using any temporaries.
But in the second case where i want o3 to be assigned the return value of the function, first a temporary is created using the copy constructor and then assignment operator is called for value assignment.
My question is what was the need for this temporary as my assignment operator takes reference. I found related questions but they didnt answer this.
test o3;
will cause a call to the constructor to create the object. C++ isn't Java where an object type declaration only declares a references but doesn't instantiate an object.
test passByref_returnByVal(test& obj) {....}
causes the copy constructor call because when inside of it you do return obj; the compiler needs to create a temporary object because the return type of that function is test (as opposed to test& or test*).
Finally, because o3 already exists courtesy of the
test o3;
declaration, the assignment operator is called to assign the return value of passByref_returnByVal to the already-existing o3.
So the copy constructor call is happening in passByref_returnByVal, not in your operator=.