I do not understand how constructors work?
Here I have declared an object obj2. It calls constructor abc(), which is perfectly fine.
But when I am assigning
obj2 = 100
Why does compiler allow initializing an integer to a class object? If at all it is allowing then how it is destroying the object and then how it is calling another parameterized constructor.
Now I have another question why destructor is called only once as there are two objects?
One more doubt I have is, compiler is not doing anything with default constructor then why default constructor is required?
class abc{
public:
int a, b;
abc()
{a = 0; b = 0;}
abc(int x)
{a = x;}
~abc()
{std::cout << "Destructor Called\n";}
};
int main()
{
abc obj1;
cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
abc obj2;
cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
obj2 = 100;
cout << "OBJ2 " << obj2.a << "\n";
system("pause");
return 0;
}
output:
OBJ1 0...0
OBJ2 0...0
Destructor Called
OBJ2 100
But when I am assigning obj2 = 100, how the compiler is allowing initializing an integer to a class object?
This is because when you do the following:
obj2 = 100;
this one will first call abc(int x) to generate an object of the class, then call the default copy assignment operator (since no user-defined is provided) to assign the value 100 to existing obj2. After the assignment, the temporary object is destructed.
If you do not desire this effect, mark the constructor as explict to avoid implicit calls.
explicit abc(int x) {
//do something
}
obj2 = 100;
You defined a constructor which takes an int. This allows for an implicit conversion from int to abc. This requires the creation of a new object. It doesn't just magically set a field in the existing object by calling a constructor; constructors construct new objects.
EDIT: Correct sequence of events from #Steve Jessop
A new instance is created, then copy-assigned to the original, then the temporary (not the original) is destroyed. The copy assignment does indeed magically set both fields in the existing object.
Let us play show and tell, so let us instrument all special members:
#include <iostream>
class abc{
public:
int a, b;
abc()
{ std::cout << "Default constructor\n"; a = 0; b = 0;}
abc(int x)
{ std::cout << "Int constructor\n"; a = x;}
abc(abc const& other): a(other.a), b(other.b)
{ std::cout << "Copy constructor (" << a << ", " << b << ")\n"; }
abc& operator=(abc const& other) {
std::cout << "Assignment operator (" << a << ", " << b << ") = (" << other.a << ", " << other.b << ")\n";
a = other.a;
b = other.b;
return *this;
}
~abc()
{std::cout << "Destructor Called\n";}
};
int main()
{
abc obj1;
std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
abc obj2;
std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
obj2 = 100;
std::cout << "OBJ2 " << obj2.a << "\n";
return 0;
}
And we obtain this output:
Default constructor
OBJ1 0...0
Default constructor
OBJ2 0...0
Int constructor
Assignment operator (0, 0) = (100, 0)
Destructor Called
OBJ2 100
Destructor Called
Destructor Called
So, let's reconcile them with line sources:
int main()
{
abc obj1;
// Default constructor
std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
// OBJ1 0...0
abc obj2;
// Default constructor
std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
// OBJ2 0...0
obj2 = 100;
// Int constructor
// Assignment operator (0, 0) = (100, 0)
// Destructor Called
std::cout << "OBJ2 " << obj2.a << "\n";
// OBJ2 100
return 0;
// Destructor Called
// Destructor Called
}
You mostly had it all, let us examine the surprises.
First surprise: even though obj2 changes value later abc obj2; will still call the default constructor at the point of declaration.
Second surprise: obj2 = 100 actually means obj2.operator=(abc(100));, that is:
Build a temporary (unnamed) abc from abc(100)
Assign it to obj2
Destroy the temporary before moving on to the next statement
Third surprise: destructors are called at the end of the scope, right before the closing bracket } (and yes, after the return). Since you are using system("pause") I assume you are on Windows => though luck they are called after you end the pause and thus your console Windows disappears in the blink of an eye at the moment they would have appeared. You can either launch the program from a more permanent console, or use an extra scope:
int main () {
{
// your code here
}
system("pause");
return 0;
}
It's because there is constructor which can take an argument of int type. That temporary created object is copied to obj2 by invoking default copy-assignment.
To avoid such conversions, mark constructor as explicit.
You're destructor is called 3 times, you can't see it because of the pause.
Related
In my application
#include <iostream>
class TestClassA
{
public:
int* m_ptr;
TestClassA(int a)
{
m_ptr = new int(a);
std::cout << "Constructor. this: " << this << " m_ptr: " << m_ptr << std::endl;
}
TestClassA(const TestClassA& copy)
{
std::cout << "Copy Constructor. copy: " << © << " -> this: " << this << std::endl;
std::cout << "Copy Constructor. old this->m_ptr: " << m_ptr << std::endl;
delete m_ptr; // not initialized pointer
m_ptr = new int;
std::cout << "Copy Constructor. new this->m_ptr: " << m_ptr << std::endl;
*m_ptr = *copy.m_ptr;
}
// passing by value, thus a copy constructor calls first
TestClassA& operator=(TestClassA tmp)
{
std::cout << "Copy assignment " << this << " <- " << &tmp << std::endl;
std::swap(m_ptr, tmp.m_ptr);
return *this;
}
~TestClassA()
{
std::cout << "Destructor " << this << std::endl;
delete m_ptr;
m_ptr = nullptr;
}
};
void testAssignment()
{
TestClassA tca1(1);
std::cout << "tca1.m_ptr: " << tca1.m_ptr << std::endl;
TestClassA tca2(2);
std::cout << "tca2.m_ptr: " << tca2.m_ptr << std::endl;
tca2 = tca1;
}
int main()
{
testAssignment();
return 0;
}
When I call assignment operator receiving arguments by value, copy constructor calls. I guess it is to create a temporary variable and to copy the state of tcs1 to it. The issue is that m_ptr member of this temporary is not initialized, so I can't delete previous m_ptr value to write a new one. What is the proper way of implementing copy constructor in this case?
Copy constructor is a constructor, not an assignment operator. The diffetence is precisely the absence of preexisting resources to destroy. You don't need to destroy anything, just initialize.
The copy constructor is called because you did not make it accept a const reference:
TestClassA& operator=(const TestClassA& tmp)
// ^ ^
It is the tmp parameter that is initialized in the example, not the this of the operator.
Of course, you'll need a local variable to get the swap trick to work, but at least it will be explicit in your code.
Below is the snippet aimed to test the constructors. It was run in VS 2015.
In my opinion, "B b(B())" has the same function as "B b = B()", however, my code seems to say that they behave different.
I know there's copy elision by compiler optimization, but I think the default constructor should be called at least when execute "B b(B())".
Can anyone help point out where my misunderstood is?
class B
{
public:
B() {
++i;
x = new int[100];
cout << "default constructor!"<<" "<<i << endl;
cout << "x address:" << x << endl << "--------" << endl;
}
B(const B &b) //copy constructor
{
++i;
cout << "Copy constructor & called " << i<< endl
<< "--------" << endl;
}
B(B &&b)//move constructor
{
x = b.x;
++i;
b.x = nullptr;
cout << "Copy constructor && called" << i << endl
<<"x address:"<< x << endl << "--------" << endl;
}
void print()
{
cout << "b address:" << x << endl << "--------" << endl;
}
private:
static int i;
int *x;
};
int B::i = 0;
int main()
{
B b1; //default constructor
b1.print();
B b2 = B(); //default constructor only
b2.print();
B b3(B()); //????nothing called... why???
B b4 = b2; //copy constructor
B b5 = move(b2); //move constructor
b2.print();
return 0;
}
Note that B b(B()) is a function declaration, not a variable definition at all, then no constructor would be called.
According to Most vexing parse, B b(B()) is a function declaration, for a function named b which returns an object of type B and has a single (unnamed) parameter which is a pointer to function returning type B and taking no parameter.
You can solve it by using braces (list initlization (since C++11)), like
B b1( B{} );
B b2{ B() };
B b3{ B{} }; // preferable from C++11
The source code :
#include <iostream>
#include <string>
using namespace std;
int counts = 0;
class A {
public:
A() {
cout << ">> A(" << ++counts << ") constructor" << endl;
}
A(const A& a) {
cout << ">> A(" << ++counts << ") copy constructor" << endl;
}
A& operator=(const A& a) {
cout << ">> A(" << ++counts << ") = constructor" << endl;
return *this;
}
};
A get_A()
{
A a1;
cout << "address of a1 = " << &a1 << endl;
return a1;
}
void test_1()
{
A a2 = get_A();
cout << "address of a2 = " << &a2 << endl;
}
int main()
{
test_1();
return 0;
}
The output :
>> A(1) constructor
address of a1 = 0x7fff5296daf8
address of a2 = 0x7fff5296daf8
My questions :
1. Why there is only one constructor invoked ? Should not the assignment constructor be invoked ?
2. Why the address of a1 and a2 are the same ?
The Return Value Optimisation (RVO) is a compiler optimisation which eliminates copying the temporary object that you create in get_a into test_1. This is why both objects have the same address - they are actually the exact same object. Your compiler is eliminating the redundant construct-and-copy, and just constructs the result in place.
Because of copy elision.
Because of copy elision.
Instead of the return value of get_A() being used to copy-construct a2, the compiler just directly allocates the return value at the call site.
You can see the behaviour you expect if you turn off copy elision in your compiler (-fno-elide-constructors in GCC and Clang).
I have a situation where no constructor appears to be called:
#include <iostream>
using namespace std;
int main ()
{
class yoyo
{
public:
int i;
yoyo()
{
i = 0;
cout << "defaultly initialized to 0" << endl;
}
yoyo (int j) : i(j)
{
cout << "initialized to " << j << endl;
}
};
int i;
yoyo a;
cout << "Hello1, i: " << a.i << endl;
yoyo b(5);
cout << "Hello2, i: " << b.i << endl;
yoyo c = b; /* 1 */
cout << "Hello3, i: " << c.i << endl;
return 0;
}
Output is:
defaultly initialized to 0
Hello1, i: 0
initialized to 5
Hello2, i: 5
Hello3, i: 5
(Note: nothing between Hello2 and Hello3)
If I change the program to read as follows:
#include <iostream>
using namespace std;
int main ()
{
class yoyo
{
public:
int i;
yoyo()
{
i = 0;
cout << "defaultly initialized to 0" << endl;
}
yoyo (int j) : i(j)
{
cout << "initialized to " << j << endl;
}
};
int i;
yoyo a;
cout << "Hello1, i: " << a.i << endl;
yoyo b(5);
cout << "Hello2, i: " << b.i << endl;
yoyo c; c = b; /* 1 */
cout << "Hello3, i: " << c.i << endl;
return 0;
}
(The only difference is in he line marked by /* 1 */)
The output now is:
defaultly initialized to 0
Hello1, i: 0
initialized to 5
Hello2, i: 5
defaultly initialized to 0
Hello3, i: 5
Now there is a constructor call between Hello2 and Hello3. My question is, why is there no (visible) constructor call in the first case?
In the case of
yoyo c = b;
it's the copy constructor that is called.
And in the case of
yoyo c; c = b;
it's the copy assignment operator that is called.
If you don't provide any of them, the compiler will generate default versions for you.
If you want to create your own copy constructor, it could look like this:
yoyo(const yoyo& other)
: i(other.i)
{ std::cout << "copy constructor initialized\n"; }
The copy assignment operator looks like this:
yoyo& operator=(const yoyo& other)
{
i = other.i;
return *this;
}
Both of them defined inside the class definition of course.
In the first case:
yoyo c = b;
calls the copy constructor, which the compiler generates for you implicitly in this case.
yoyo c = b;
This is referred to as copy-initialization; the compiler-generated copy constructor will be called and c will be initialized with that copy. Also, the default constructor of c will be called.
c = b;
Here, this is not initialization, this is assignment. The compiler-generated assignment operator will be invoked on that line.
in your code,
yoyo c=b will call copy constructor.if you want to see it being called,you have to define it explicitely.
eg:
yoyo(const yoyo& obj)
{
this->i=obj.i; cout<<"copy constructor"<<endl;
}
in the second case it will call constructor and then the assignment operator
yoyo c; //constructor
c = b; //assignment operator for which only copying occurs
you can overload assignment operator as below
yoyo& operator=(yoyo& obj)
{
i = obj.i;
cout << "assignment operator" << endl;
}
There is such code:
#include <iostream>
class A {
public:
int a;
A() : a(0) {
std::cout << "Default constructor" << " " << this << std::endl;
}
A(int a_) : a(a_) {
std::cout << "Constructor with param " << a_ << " " << this << std::endl;
}
A(const A& b) {
a = b.a;
std::cout << "Copy constructor " << b.a << " to " << a << " " << &b << " -> " << this << std::endl;
}
A& operator=(const A& b) {
a=b.a;
std::cout << "Assignment operator " << b.a << " to " << a << " " << &b << " -> " << this << std::endl;
}
~A() {
std::cout << "Destructor for " << a << " " << this << std::endl;
}
void show(){
std::cout << "This is: " << this << std::endl;
}
};
A fun(){
A temp(3);
temp.show();
return temp;
}
int main() {
{
A ob = fun();
ob.show();
}
return 0;
}
Result:
Constructor with param 3 0xbfee79dc
This is: 0xbfee79dc
This is: 0xbfee79dc
Destructor for 3 0xbfee79dc
Object ob is initialized by function fun(). Why copy constructor is not called there? I thought that when function returns by value then copy constructor or assignment operator is called. It seems that object constructed in function fun() is not destroyed after execution of function. How can be copy constructor forced to invoke in this case?
This was compiled by g++.
Why copy constructor is not called there?
RVO
How can be copy constructor forced to invoke in this case?
Pass an option to the compiler. For gcc, it is --no-elide-constructors option to disable the RVO
That is called Named Return Value Optimization and copy elision, and basically means that the compiler has figured out that the copy can be avoided by carefully placing the temporary and the object in the same memory location.
By default there would be three objects in that piece of code, temp inside fun, the return value and ob inside main, and as many as two copies, but by carefully placing temp in the same memory location as the returned object inside fun and placing ob in the same memory address the two copies can be optimized away.
I wrote about those two optimizations with a couple of pictures to explain what is going on here:
NRVO
Copy elision