I created a basic test class to learn how the move constructor works. The move constructor does not seem to be called, and I'm not sure what constructor is actually called. If I use std::move then the move constructor is called, but a regular R value won't actually call it. Why is this happening, and what constructor is actually being called? I'm using g++ 4.6.3
#include <iostream>
#include <cstring>
class Test
{
int a;
int b;
int* c;
public:
Test(int one, int two)
{
std::cout << "Param Constructor" << "\n";
a = one;
b = two;
}
Test()
{
std::cout << "Default Constructor" << "\n";
a = 1;
b = 2;
}
~Test()
{
std::cout << "Deconstructor" << "\n";
}
Test(const Test& test)
{
a = test.a;
b = test.b;
std::cout << "Copy constructor called" << "\n";
}
Test(Test&& test)
{
std::cout << "Move constructor called" << "\n";
a = test.a;
b = test.b;
}
Test& operator=(const Test& test)
{
std::cout << "in operator=" << "\n";
a = test.a;
b = test.b;
return *this;
}
};
Test createTest()
{
return Test(1,2);
}
int main()
{
Test test(createTest());
Test test2 = test;
std::cout << "After logic" << "\n";
return 0;
}
The output I get:
Param Constructor
Copy constructor called
After logic
Deconstructor
Deconstructor
I create an object of type Test with the name test, but there is no output of it being created? I was expecting this output:
Param Constructor
Move Constructor // (Missing)
Deconstructor //Deleting instance from createTest (Missing)
Copy constructor called
After logic
Deconstructor
Deconstructor
what constructor is actually being called?
No constructor at all. Count the number of destructor calls. You'll find that there is one less than you expected. The temporary that you expected to be constructed wasn't created at all.
Why is this happening
The compiler avoided creation of the temporary. Instead, the object was constructed in place where it would have been moved to otherwise. This is known as copy elision.
Related
The following code snippet which I was writing to understand move CTOR behaviour is giving me hard time to understand it's output:
#include <iostream>
class Temp
{
public:
Temp(){
std::cout << "Temp DEFAULT CTOR called" << std::endl;
mp_Val = nullptr;
}
Temp(int inp) {
std::cout << "Temp CTOR called" << std::endl;
mp_Val = new int(inp);
}
Temp(const Temp& inp) {
std::cout << "Temp COPY CTOR called" << std::endl;
mp_Val = new int(*inp.mp_Val);
}
Temp& operator= (const Temp& inp) {
std::cout << "Temp ASSIGNMENT OPER called" << std::endl;
mp_Val = new int(*inp.mp_Val);
return *this;
}
int* mp_Val;
};
class B
{
public:
B(){
std::cout << "Class B DEFAULT CTOR" << std::endl;
mp_Val = nullptr;
}
B(int inp) {
std::cout << "Class B CTOR" << std::endl;
mp_Val = new Temp(inp);
}
B(const B& in) {
std::cout << "Class B COPY CTOR" << std::endl;
mp_Val = in.mp_Val;
}
B(B&& in){
std::cout << "Class B MOVE CTOR" << std::endl; //Doubt: 1
}
Temp *mp_Val;
};
int main() {
B obj1(200);
B obj2 = std::move(obj1);
auto temp = obj1.mp_Val;
std::cout << "Obj1 B::Temp address: " << obj1.mp_Val << std::endl;
std::cout << "Obj2 B::Temp address: " << obj2.mp_Val << std::endl; //Doubt: 2
return 0;
}
Output:
Class B CTOR
Temp CTOR called
Class B MOVE CTOR
Obj1 B::Temp address: 0xd48030
Obj2 B::Temp address: 0x400880
GCC version: 4.6.3
My question is about the line marked as Doubt 2. Should not the address be printed as 0? As per my understanding, as I have defined an empty move CTOR (marked as Doubt 1) in class B, it should call the default CTOR of class Temp (which it's not calling as evident from the logs) to initialise its member variable mp_Val of type Temp.
There is obviously something that I am missing.
As per my understanding, as I have defined an empty move CTOR (marked as Doubt 1) in class B, it should call the default CTOR of class Temp (which it's not calling as evident from the logs) to initialise its member variable mp_Val of type Temp.
Your member variable isn't of type Temp, it's of type Temp *. You're right that the lack of an initialiser means that that member will be default-constructed, and for type Temp that would involve calling the default constructor. However, for pointer types, default construction leaves the object uninitialised.
I am not able to understand how the values of member variables are getting copied even though the constructor is not getting called in the below program.
#include <iostream>
using namespace std;
class myclass
{
public:
int x;
int y;
myclass(int a, int b)
{
cout << "In Constructor" << endl;
x = a;
y = b;
}
~myclass()
{
cout << "In Destructor" << endl;
}
myclass(const myclass &obj)
{
cout << "In Copy Constuctor " << obj.x << " " << obj.y << endl;
x = obj.x;
y = obj.y;
}
myclass &operator=(const myclass &obj)
{
cout << "In Operator Overloading" << obj.x << obj.y << endl;
x = obj.x;
y = obj.y;
return *this;
}
};
int main()
{
myclass obj1 = myclass(2, 3);
cout << "obj1.x : " << obj1.x << "obj1.y" << obj1.y << endl;
}
Output:
In Constructor
obj1.x : 2obj1.y3
In Destructor
I understood that due to Return Value Optimization, copy constructor is not getting called. But I didn't understand how obj1 is getting values 2 and 3. Can any one please help me to understand this or how Return Value Optimization will work behind the scenes.
The values aren't getting copied, because they don't need to be copied. Instead, the values which would have been copied are initialized in place. Copy elision means that the compiler essentially turns this:
myclass obj1 = myclass(2, 3);
Into this:
myclass obj1(2, 3);
So no extra object is constructed which needs to be copied.
Note that RVO, which you've referred to, is a form of copy elision. But this specific case of copy elision is not RVO.
I wrote below code and I didn't understand why copy constructor is getting called.
#include <iostream>
using namespace std;
class abc
{
public:
abc()
{
cout << "in Construcutor" << (this) << endl;
};
~abc()
{
cout << "in Destrucutor" << (this) << endl;
};
abc(const abc &obj)
{
cout << "in copy constructor" << (this) << endl;
cout << "in copy constructor src " << &obj << endl;
}
abc& operator=(const abc &obj)
{
cout << "in operator =" << (this) << endl;
cout << "in operator = src " << &obj << endl;
}
};
abc myfunc()
{
static abc tmp;
return tmp;
}
int main()
{
abc obj1;
obj1 = myfunc();
cout << "OK. I got here" << endl;
}
when I ran this program, I am getting the following output
in Construcutor0xbff0e6fe
in Construcutor0x804a100
in copy constructor0xbff0e6ff
in copy constructor src 0x804a100
in operator =0xbff0e6fe
in operator = src 0xbff0e6ff
in Destrucutor0xbff0e6ff
OK. I got here
in Destrucutor0xbff0e6fe
in Destrucutor0x804a100
I didn't understand why copy constructor is getting called when I was actually assigning the object.
If I declare abc tmp, instead of static abc tmp in myfunc(), then copy constructor is not getting called. Can any one please help me to understand what is going on here.
Because you return an object by value from myfunc, which means it's getting copied.
The reason the copy-constructor is not getting called if you don't make the object static in myfunc is because of copy elision and return value optimization (a.k.a. RVO). Note that even though your copy-constructor may not be called, it still has to exist.
If I modify the assignment opreator so that it returns an object A instead of a reference to an object A then something funny happens.
Whenever the assignment operator is called, the copy constructor is called right afterwards. Why is this?
#include <iostream>
using namespace std;
class A {
private:
static int id;
int token;
public:
A() { token = id++; cout << token << " ctor called\n";}
A(const A& a) {token = id++; cout << token << " copy ctor called\n"; }
A /*&*/operator=(const A &rhs) { cout << token << " assignment operator called\n"; return *this; }
};
int A::id = 0;
A test() {
return A();
}
int main() {
A a;
cout << "STARTING\n";
A b = a;
cout << "TEST\n";
b = a;
cout << "START c";
A *c = new A(a);
cout << "END\n";
b = a;
cout << "ALMOST ENDING\n";
A d(a);
cout << "FINAL\n";
A e = A();
cout << "test()";
test();
delete c;
return 0;
}
The output is as follows:
0 ctor called
STARTING
1 copy ctor called
TEST
1 assignment operator called
2 copy ctor called
START c3 copy ctor called
END
1 assignment operator called
4 copy ctor called
ALMOST ENDING
5 copy ctor called
FINAL
6 ctor called
test()7 ctor called
Because if you don't return a reference of the object it makes a copy.
As #M.M said about the final test() call, the copy does not appears because of the copy elision What are copy elision and return value optimization?
Here is my min ex:
#include <iostream>
#include <vector>
class A {
public:
A() { std::cout << "Constructor\n"; }
~A() { std::cout << "Destructor\n"; }
};
class B {
public:
B() { v.push_back(A()); v.push_back(A()); }
private:
std::vector<A> v;
};
int main() {
B b;
return 0;
}
So, I got this output:
Constructor // first push back
Destructor // copy destroyed
Constructor // second push back
Destructor // copy destroyed
Destructor // ???????????????
// B object goes out of scope and its vector too...
Destructor // v[0] destructor called
Destructor // v[1] destructor called
Can someone shed some light please?
After following Dave's comment:
Constructor
Copy constructor
Destructor
Constructor
Copy constructor
Copy constructor
Destructor
Destructor
Destructor
Destructor
Adding in an overloaded copy constructor and some indicator of which object is being acted upon sheds some light on the situation:
#include <iostream>
#include <vector>
class A {
public:
A(unsigned i): i(i) { std::cout << "Constructor " << i << std::endl; }
A(const A& a) : i(a.i) { std::cout << "Copy constructor " << i << std::endl; }
~A() { std::cout << "Destructor " << i << std::endl; }
unsigned i;
};
class B {
public:
B() { v.push_back(A(0)); v.push_back(A(1)); }
private:
std::vector<A> v;
};
int main() {
B b;
return 0;
}
On the first push, we make a copy and destroy the temporary. On the second push, we make a copy, then copy the first object, before destroying the first object and the temporary. Finally, we destroy both objects.
I'd guess that the std::vector was first allocated with a capacity of 1, so the second push forced a re-allocation? If I force a larger initial capacity (by calling v.reserve(5) before the first push), then the extra copy disappears.
You are not tracking construction of As using the default copy constructor. If you add a copy constructor and a message in it, the number of calls to constructors should match the number of calls to the destructor.
Modifying declaration of class A as follows,
class A
{
public:
A() { std::cout << "Constructor " << this << std::endl; }
A(const A&) { std::cout << "Copy Constructor " << this << std::endl; }
~A() { std::cout << "Destructor " << this << std::endl; }
};
running the program at http://coliru.stacked-crooked.com/
gives output:
Constructor 0x7fffecb8dc0f
Copy Constructor 0x1efdc20
Destructor 0x7fffecb8dc0f
Constructor 0x7fffecb8dc0e
Copy Constructor 0x1efdc41
Copy Constructor 0x1efdc40
Destructor 0x1efdc20
Destructor 0x7fffecb8dc0e
Destructor 0x1efdc40
Destructor 0x1efdc41
which clearly shows the construction and destruction of objects , both on stack and on heap (0x1efdc20, 0x1efdc40, and 0x1efdc41 are locations of the objects allocated by the vector).