I'm having difficulty wrapping my mind around the following (specifically, scenario b):
(Assume I have defined an assignment operator, addition operator, and copy constructor just to output the fact that they are being called)
scenario a:
Simple a;
Simple b;
Simple c = a + b;
The output is as follows:
Simple constructor called
Simple constructor called
Simple add operator call
Simple constructor called
copy constructor called
-- This is all fine and dandy
scenario b (the behavior that I cannot understand):
Simple d;
Simple e;
Simple f;
f = d + e;
Simple constructor called
Simple constructor called
Simple constructor called
Simple add operator called
Simple constructor called
copy constructor called
assignment operator called
The question that I have is that in scenario b, why is the copy constructor called right before the assignment operator is? To my understanding, a copy constructor will only be called on an uninitialized object. However, in this scenario, the object f has been initialized in the line preceding the addition.
An explanation would be greatly appreciated.
Apologies for not posting the source code right away (and for the lack of indentation - I am having problems copying to the textarea). Here it is in all of it's simplicity.
I am using Visual Studio 2005. Unfortunately, I am not that familiar with the workings of it yet, hence I cannot specify the optimization parameters that are being passed to the compiler.
class Simple
{
public:
Simple(void);
Simple operator +(const Simple& z_Simple) const;
Simple& operator =(const Simple& z_Simple);
Simple(const Simple& z_Copy);
int m_Width;
int m_Height;
public:
~Simple(void);
};
#include "Simple.h"
#include <iostream>
using std::cout;
using std::endl;
Simple::Simple(void)
{
this->m_Height = 0;
this->m_Width = 0;
cout << "Simple constructor called" << endl;
}
Simple::Simple(const Simple& z_Copy)
{
cout << "copy constructor called" << endl;
this->m_Height = z_Copy.m_Height;
this->m_Width = z_Copy.m_Width;
}
Simple& Simple::operator =(const Simple &z_Simple)
{
cout << "assignment operator called" << endl;
this->m_Height = z_Simple.m_Height;
this->m_Width = z_Simple.m_Width;
return *this;
}
Simple Simple::operator +(const Simple &z_Simple) const
{
cout << "Simple add operator called" << endl;
int y_Height = this->m_Height + z_Simple.m_Height;
int y_Width = this->m_Width + z_Simple.m_Width;
Simple y_Ret;
y_Ret.m_Height = y_Height;
y_Ret.m_Width = y_Width;
return y_Ret;
}
Simple::~Simple(void)
{
cout << "destructor called" << endl;
}
Certainly Nemo's explanation is the one that my novice C++ mind can grasp :)
After changing the optimization level to /O2, I can see the output of scenario b as follows (and what I would have expected)
Simple constructor called
Simple constructor called
Simple constructor called
Simple add operator called
Simple constructor called
assignment operator called
Thank you all for your suggestions.
Your + operator returns a object by value, which might result in call to copy constructor if the compiler did not elide it.
Simple Simple::operator +(const Simple &z_Simple) const
{
//......
Simple y_Ret;
//......
return y_Ret;
}
Code:
Simple d;
Simple e;
Simple f;
f = d + e;
Here is a step by step analysis:
Simple constructor called ---> creation of `d`
Simple constructor called ---> creation of `e`
Simple constructor called ---> creation of `f`
Simple add operator called ---> Inside Addition operator
Simple constructor called ---> creation of local `y_Ret`
copy constructor called ---> `y_Ret` returned by value
assignment operator called ---> Result returned by `+` used for `=`
Related
I am trying to get the grasp of rvalue references and move semantics with a simple self-made example but I can't understand a specific part. I have created the following class:
class A {
public:
A(int a) {
cout << "Def constructor" << endl;
}
A(const A& var) {
cout << "Copy constructor" << endl;
}
A(A&& var) {
cout << "Move constructor" << endl;
}
A& operator=(const A& var) {
cout << "Copy Assignment" << endl;
return *this;
}
A& operator=(A&& var) {
cout << "Move Assignment" << endl;
return *this;
}
};
I tried the following experiments to see if I can predict how the constructors/operators are going to be called:
A a1(1) - The default constructor is going to be called.
PREDICTED.
A a2 = a1 - The copy constructor is going to be called. PREDICTED.
a1 = a2 - The copy assignment operator is going to be called.
PREDICTED.
Now, I created a simple function that just returns an A object.
A helper() {
return A(1);
}
A a3 = helper() - The default constructor is going to be called in
order to create the object that the helper returns. The move
constructor is not going to be called due to RVO. PREDICTED.
a3 = helper() - The default constructor is going to be called in
order to create the object that the helper returns. Then, the move
assignment operator is going to be called. PREDICTED.
Now comes the part I don't understand. I created another function that is completely pointless. It takes an A object by value and it just returns it.
A helper_alt(A a) {
return a;
}
A a4 = helper_alt(a1) - This will call the copy constructor, to
actually copy the object a1 in the function and then the move
constructor. PREDICTED.
a4 = helper_alt(a1) - This will call the copy constructor, to
actually copy the object a1 in the function and then I thought that
the move assignment operator is going to be called BUT as I saw,
first, the move constructor is called and then the move assignment
operator is called. HAVE NO IDEA.
Please, if any of what I said is wrong or you feel I might have not understood something, feel free to correct me.
My actual question: In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?
Congratulations, you found a core issue of C++!
There are still a lot of discussions around the behavior you see with your example code.
There are suggestions like:
A&& helper_alt(A a) {
std::cout << ".." << std::endl;
return std::move(a);
}
This will do what you want, simply use the move assignment but emits a warning from g++ "warning: reference to local variable 'a' returned", even if the variable goes immediately out of scope.
Already other people found that problem and this is already made a c++ standard language core issue
Interestingly the issue was already found in 2010 but not solved until now...
To give you an answer to your question "In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?" is, that also C++ committee does not have an answer until now. To be precise, there is a proposed solution and this one is accepted but until now not part of the language.
From: Comment Status
Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.
consider the below example. I have compiled the sample code using -fno-elide-constructors flag to prevent RVO optimizations:
g++ -fno-elide-constructors -o test test.cpp
#include<iostream>
using namespace std;
class A {
public:
A(int a) {
cout << "Def constructor" << endl;
}
A(const A& var) {
cout << "Copy constructor" << endl;
}
A(A&& var) {
cout << "Move constructor" << endl;
}
A& operator=(const A& var) {
cout << "Copy Assignment" << endl;
return *this;
}
A& operator=(A&& var) {
cout << "Move Assignment" << endl;
return *this;
}
};
A a_global(1);
A helper_alt(A a) {
return a;
}
A helper_a_local(A a) {
A x(1);
return x;
}
A helper_a_global(A a) {
return a_global;
}
int main(){
A a1(1);
A a4(4);
std::cout << "================= helper_alt(a1) ==================" << std::endl;
a4 = helper_alt(a1);
std::cout << "=============== helper_a_local() ================" << std::endl;
a4 = helper_a_local(a1);
std::cout << "=============== helper_a_global() ================" << std::endl;
a4 = helper_a_global(a1);
return 0;
}
This will result in the below output:
Def constructor
Def constructor
Def constructor
================= helper_alt(a1) ==================
Copy constructor
Move constructor
Move Assignment
=============== helper_a_local() ================
Copy constructor
Def constructor
Move constructor
Move Assignment
=============== helper_a_global() ================
Copy constructor
Copy constructor
Move Assignment
In simple words, C++ constructs a new temporary object (rvalue) when the return type is not a reference, which results in calling Move or Copy constructor depending on the value category and the lifetime of the returned object.
Anyway, I think the logic behind calling the constructor is that you are not working with reference, and returned identity should be construed first, either by copy or move constructor, depending on the returned value category or lifetime of the return object. As another example:
A helper_move_vs_copy(A a) {
// Call the Copy Constructor
A b = a;
// Call the Move Constructor, Due to the end of 'a' lifetime
return a;
}
int main(){
A a1(1);
A a2(4);
std::cout << "=============== helper_move_vs_copy() ================" << std::endl;
helper_move_vs_copy(a1);
return 0;
}
which outputs:
Def constructor
Def constructor
=============== helper_move_vs_copy() ================
Copy constructor
Copy constructor
Move constructor
From cppreference:
an xvalue (an “eXpiring” value) is a glvalue that denotes an object whose resources can be reused;
At last, it is the job of RVO to decrease unnecessary moves and copies by optimization of the code, which can even result in an optimized binary for basic programmers!
I wanted to ask a question about operator overloading in C++.
I am a beginner in C++ and have been learning OOP.
Consider the following overloaded copy assignment operator:
class Base {
private:
int value;
public:
Base () : value {0}
{
cout << "Base No-args constructor" << endl;
}
Base (int x) : value {x} {
cout << "Base (int) overloaded constructor" << endl;
}
Base (const Base &other) : value {other.value} {
cout << "Base copy constructor" << endl;
}
Base & operator = (const Base &rhs) {
cout << "Base operator=" << endl;
if (this == &rhs)
return *this;
value = rhs.value;
return *this;
}
~ Base () {cout << "Base Destructor" << endl;}
};
I wanted to clarify two points.
How does this copy assignment operator work?
Does the reference before the operator keyword need a parameter name?
I wanted to give my interpretation on (1).
If I have the following code in my main():
Base b {100}; // overloaded constructor
Base b1 {b}; // copy constructor
b = b1; // copy assignment
What I think happens is that the no args constructor is called for a1, evidently because no arguments are passed into the construction of the object.
When a2 is initialised, a temporary copy of a1 is made and then the operator is evaluated with respect to the Base class, hence the Base overloaded copy assignment block is run, and the a1 object is returned via return *this to the reference a2.
To explain my thoughts on the second question,
I thought that all parameters need a name when a function or method is declared (I may of course be wrong).
If my overloaded copy assignment block was written hypothetically as:
Base &lhs operator = (const Base &rhs)
am I right in saying that lhs is referring to a2 but as we don't do anything with lhs due to the implied this parameter, we don't need to give a parameter name following the ampersand before the operator?
How does this copy assignment operator work?
Redacted:
Base& operator=(Base const& rhs) {
cout << "Base operator=\n";
value = rhs.value;
return *this;
}
The assignment operator is just a function call. In this particular class's case, the compiler will synthesize one that does the right thing, but if you want the cout side-effect for educational purposes you've done the right thing.
You could also call it in a method function style:
b.operator=(b1);
The b = b1; is just syntactic sugar for the above.
The assignment operator should be self-assignment safe. But general guidance is to make it safe without checking for the self-assignment case explicitly, which is "bad" because it optimizes for the pathological case.
In your implementation, it is safe without the self-assignment check.
I prefer specifying the qualifier in Base const& order rather than const Base& order, because the general rule is "the qualifier always binds to the thing to its immediate left", and the exception to the general rule is "...unless the qualifier comes first, in which case it then binds to the thing to its immediate right." That becomes a problem when people have the grasped the exception to the rule, but not the general rule, and then have troubles parsing Base*const* in their heads.
Does the reference before the operator keyword need a parameter name?
It's name is *this. It is a return type, not a parameter, so it does not have a parameter name.
Some people have troubles with "Why does C++ use this as a pointer to itself, rather than as a reference to itself."
It is a historical anomaly from the evolution of C++. Originally, there was a this pointer, and references had not been added to the language yet.
With 20/20 hindsight, this would have been a reference. But that ship has sailed.
In my own code, only to help make it more legible, I'll do:
auto& self = *this;
self[i] = 5;
...rather than the more confusing (imo)...
(*this)[i] = 5;
When I read about copy constructor and copy assignment constructor, what I understood is that both gives away their properties one to another, and that both of them are declared implicitly by compiler (if not defined). So both must be exist whether doing something useful or not.
Then I tested this code:
#include <iostream>
using namespace std;
class Test {
public:
Test () {
cout << "Default constructor" << endl;
}
Test (const Test& empty) {
cout << "Copy constructor" << endl;
}
Test& operator=(const Test& empty) {
cout << "Copy assignment operator" << endl;
}
private:
};
int main () {
Test a; // Test default constructor
Test b (a); // Test copy constructor
Test c = b; // Test copy assignment constructor
system ("pause");
return 0;
}
But it seems the copy assignment operator is not called at all. I tried with three conditions:
Everything included. It prints out:
// Default constructor
// Copy constructor
// Copy constructor # Why not prints out "Copy assignment operator"?
Whitout copy assignment operator just copy constructor. It prints out:
// Default constructor
// Copy constructor
// Copy constructor # Why it's printed out even though I didn't define it?
Whitout copy constructor just copy assignment operator. It prints out:
// Default constructor # Why "Copy assignment operator" is not printed out?
Only constructor. It prints out:
// Default constructor # Understandable
So, it's like the compiler doesn't even care if I define the copy assignment operator or not. None of the four examples above prints out "Copy assignment operator". So when did it get called, if it is really exists and has a meaning?
Test c = b is an initialization, not an assignment.
If you had done this:
Test c;
c = b;
Then it would have called the copy assignment operator.
When I read about copy constructor and copy assignment constructor, what I understood is that both gives away their properties one to another, and that both of them are declared implicitly by compiler (if not defined). So both must be exist whether doing something useful or not.
Then I tested this code:
#include <iostream>
using namespace std;
class Test {
public:
Test () {
cout << "Default constructor" << endl;
}
Test (const Test& empty) {
cout << "Copy constructor" << endl;
}
Test& operator=(const Test& empty) {
cout << "Copy assignment operator" << endl;
}
private:
};
int main () {
Test a; // Test default constructor
Test b (a); // Test copy constructor
Test c = b; // Test copy assignment constructor
system ("pause");
return 0;
}
But it seems the copy assignment operator is not called at all. I tried with three conditions:
Everything included. It prints out:
// Default constructor
// Copy constructor
// Copy constructor # Why not prints out "Copy assignment operator"?
Whitout copy assignment operator just copy constructor. It prints out:
// Default constructor
// Copy constructor
// Copy constructor # Why it's printed out even though I didn't define it?
Whitout copy constructor just copy assignment operator. It prints out:
// Default constructor # Why "Copy assignment operator" is not printed out?
Only constructor. It prints out:
// Default constructor # Understandable
So, it's like the compiler doesn't even care if I define the copy assignment operator or not. None of the four examples above prints out "Copy assignment operator". So when did it get called, if it is really exists and has a meaning?
Test c = b is an initialization, not an assignment.
If you had done this:
Test c;
c = b;
Then it would have called the copy assignment operator.
I am confused over the differences between passing objects by reference and by value to functions of a particular class. If I pass objects by value, I know that the default copy constructor makes a member-by-member copy of the the object for use in the given function. However, if I am passing objects as a const reference for a class that requires deep copy, is the copy constructor still called? Say that I had a function
void debug(const MyClass& object1);
Would passing object1 call the copy constructor? Or is the object passed into the function directly without having a copy made? One more question - If I have a class called Fraction-
Fraction A(1,2); // 1 is this numerator, 2 the denominator
A = Fraction(2,3);
Does the aforementioned line call the default constructor to make a temporary object Fraction(2,3) and then the assignment operator?
Thanks.
Would passing object1 call the copy constructor?
No, it will not call the copy constructor since passed by reference
No copy is made in this case
A = Fraction(2,3);
Yes, it will call the constructor with two parameters (or default constructor if both parameters have default values), then call the copy assignment operator.
You can see the output from code below:
#include <iostream>
using namespace std;
class Fraction
{
public:
int denom;
int nominator;
Fraction(int d , int n ):denom(d), nominator(n)
{
cout << "call non-copy constructor" <<endl;
}
Fraction(const Fraction& rhs)
{
cout << "call copy constructor" <<endl;
denom = rhs.denom;
nominator = rhs.nominator;
}
const Fraction& operator=(const Fraction& rhs)
{
cout << "call copy assignment operator" << endl;
if (this == &rhs)
{
return *this;
}
denom = rhs.denom;
nominator = rhs.nominator;
return *this;
}
};
void debug(const Fraction& obj)
{
cout << "this is debug: pass by reference " <<endl;
}
void debugPassByValue(Fraction obj)
{
cout << "this is debug: pass by value" <<endl;
}
int main()
{
Fraction A(1,2);
cout << "--------------" <<endl;
debug(A);
cout << "--------------" <<endl;
A = Fraction(2,3);
cout << "--------------" <<endl;
debugPassByValue(A);
cout << "--------------" <<endl;
cin.get();
return 0;
}
You will see the following output:
call non-copy constructor //Fraction A(1,2);
--------------
this is debug: pass by reference //debug(A);
--------------
call non-copy constructor //A = Fraction(2,3);---> construct temporary object
call copy assignment operator //A = Fraction(2,3);
--------------
call copy constructor //debugPassByValue(A);
this is debug: pass by value
--------------
Now you will have a clearer view of what are called.
In the following we will consider [x] to mean that x is optional.
I am confused over the differences between passing objects by reference and by value to functions of a particular class.
When you pass an object by value, the program must create the local object of the function therefore it calls the copy constructor of the class to create this object. When you pass by reference (and seemingly by pointer) the variable object1 inside the function is just an alias of the object you passed to the function; therefore if you edit the one inside the function, the edits will be applied to the outside object as well.
Does the aforementioned line call the default constructor to make a temporary object Fraction(2,3) and then the assignment operator?
It's the assignment operator. Considering A to be an already declared variable of type X it will be called X Fraction::operator=([const] Fraction[&]) or any compatible type.
Notice: when declaring Fraction x = Fraction(2, 3) it won't be used the operator= as you may expect, the corresponding constructor will be called instead (in this case Fraction::Fraction([const] Fraction[&])).
Indeed in the case of debug no copy is made.
In the second case, I'm not quite sure I understand your question. This line:
A = Fraction(2,3);
Should use Fraction's assignment operator. A already exists, so it uses the assignment operator on instance A to assign it Fraction(2,3) which is a temporary object.