#include<iostream>
using namespace std;
class Test
{
private:
int x;
public:
Test(int i)
{
cout<<"Conversion constructor called..."<<endl;
x = i;
}
~Test()
{
cout<<"Destructor called..."<<endl;
}
void show()
{
cout<<" x = "<<x<<endl;
}
};
int main()
{
Test t(20);
t.show();
t = 30;
t.show();
return 0;
}
output:
Conversion constructor called...
x = 20
Conversion constructor called...
Destructor called...
x = 30
Destructor called...
When i am doing t = 30 why it is calling constructor and destructor?
Please explain.Many thanks in advance.
There is no overload of = that can be used to assign directly from an int value. Your conversion constructor allows 30 to be converted to a Test object, which can then be assigned using the implicitly-generated copy constructor (which copies each member). So your assignment is equivalent to
t = Test(30);
creating and destroying a temporary object to assign from.
You could avoid this by providing an assignment operator:
Test & operator=(int i) {x = i;}
in which case the assignment could use this directly, equivalent to
t.operator=(30);
When you write t = 30, your compiler creates a temporary Test variable, which it creates using the conversion constructor. After setting t equal to this temporary variable, the temporary variable is then destroyed, calling the destructor.
Because t is already defined, it cannot be initialized throug the convertion contructor.
Instead, it creates a temporay Test object, calls the operator=, then delete it.
"why it is calling constructor and destructor?"
Because there's a (implicit) temporary instance of Test constructed, assigned to t and destructed after assignment.
Related
It is said that constructor doesnot return anything.But if constructor doesnot return anything, then how do this code segment works:
*this=classname{args};.
I hope someone could shed me some light on whats actually going under the hood.
A complete code:
#include <iostream>
using namespace std;
class hell {
private:
int a{}, b{};
public:
hell(int _a = 7, int _b = 8) : a{ _a }, b{ _b } {}
void print() {
cout << a << endl << b << endl;
}
void change() {
*this = hell(4, 5);
}
};
int main() {
hell h;
h.print();
h.change();
h.print();
return 0;
}
The statement
*this = hell(4, 6);
does two things:
First it create a temporary and unnamed object of the class hell, initialized using the values you use to pass to a suitable constructor.
Then that temporary and unnamed object is copy-assigned to the object pointed to by this.
It's somewhat similar to this:
{
hell temporary_object(4, 5); // Create a temporary object
*this = temporary_object; // Copy the temporary object to *this
}
Your question must be about this line:
*this = hell(4,5);
This might look like a function call, calling the constructor function. But this is not a function call. This is (a particular) syntax for creating a new hell object, a temporary object. This is slightly outdated syntax, modern C++ prefers:
*this = hell{4, 5};
but it's the same thing.
This does call the constructor, but only as part of constructing a new object.
Once the temporary object gets constructed it gets assigned to *this. The End.
For better intuition, constructor can be called "initializator". This reveals more of it's main purpose. The constructor initializes object rather then creates. So the instance of the object is already present in the memory when the constructor is called. All it does is initializing it.
class sampleConstructor {
int x;
public:
//WE COULD DO OVERLOAD CONSTRUCTOR JUST LIKE IN FUNCTIONS | THEY ONLY DIFFERENTIATE IN NO. OF ARGUMENTS AND DATATYPE OF ARGUMENTS JUST LIKE IN FUNCTION
sampleConstructor () { //THIS IS THE DEFAULT CONSTRUCTOR, THIS WILL AUTOMATICALLY INITIALIZE EVERYTHING TO 0 IF NOT EXPLICITLY STATED, THIS IS AUTOMATICALLY CREATED UNLESS EXPLICITLY STATED
x = 0;
}
//sampleConstructor () { }; //IT COULD ALSO LOOK LIKE THIS
//IF YOU CREATED A PARAMETERIZED CONSTRUCTOR, DEFAULT CONSTRUCTOR WOULD NOT BE AUTOMATICALLY CREATED ANYMORE
sampleConstructor (int y) { //THIS IS A PARAMETERIZED CONSTRUCTOR
x = y;
}
//COPY CONSTRUCTOR ARE PASSED BY REFERENCE AS TO AVOID INFINITE RECURSION
sampleConstructor (sampleConstructor &sampleCopy) { //THIS IS COPY CONSTRUCTOR, THIS IS AUTOMATICALLY CREATED UNLESS EXPLICITLY STATED | PURPOSE OF COPY CONSTRUCTOR IS TO COPY THE VALUE OF ANOTHER OBJECT
x = sampleCopy.x;
}
void showData () {
std::cout << "value of x is " << x << std::endl;
}
//IT IS NOT A GOOD PRACTICE TO CALL DESTRUCTOR EXPLICITLY
// ~sampleConstructor () { } ; //THIS IS DESTRUCTOR, IT IS AUTOMATICALLY CREATED BY THE COMPILER, IT MUST CONTAIN NO ARGUMENT, ONLY ONE DESTRUCTOR IS REQUIRED.
~sampleConstructor () {
std::cout << "yes, sample constructor could also do this!" << std::endl;
}
};
int main () {
//EACH OBJECT CAN ONLY USE 1 TYPE OF CONSTRUCTOR
sampleConstructor obj1(50); //HERE WE USES PARAMETERIZED CONSTRUCTOR
sampleConstructor obj4 =sampleConstructor(50); //OBJECT CAN ALSO BE INITIALIZED LIKE THIS
sampleConstructor obj2(obj1); //HERE WE USES COPY CONSTRUCTOR, WE COPY THE VALUE OF obj1 INTO obj2
sampleConstructor obj3 = obj1; //COPY CONSTRUCTOR CAN ALSO BE INITIALIZED LIKE THIS
obj1.showData();
obj2.showData();
obj3.showData();
return (0);
}
obj4 is producing an error, it is confused whether to use parameterized constructor or copy constructor, but when i initialize it like this: sampleConstructor obj4(20), it perfectly works. sampleConstructor obj4 = sampleConstructor(20) is the same with sampleConstructor obj4(20), right?
The parameter of the copy constructor needs to be a const reference:
sampleConstructor(const sampleConstructor &sampleCopy)
Non-const (lvalue) references can't bind to rvalues, and sampleConstructor(50) is an rvalue.
Note that your code is valid as is starting from C++17, which requires sampleConstructor obj4 =sampleConstructor(50); to be exactly equivalent to sampleConstructor obj4(50);. Pre-C++17 the compilers were allowed to not emit a copy (or move) constructor call in this case, but it was required for the copy (or move) constructor itself to be available, even the compiler decided to not use it.
Assuming no return value optimizaiton in compiler.
In c++, when the object return-by-vaule in function, the real step is below, am I correct? Totally, call constructor third time.
local(normal constuctor) --> temp(copy constructor) ---> outside(copy constructor or copy assignemt operator)
created a local object, here it calls normal constructor;
created a temp object using local object, here it calls copy constructor;
assign the temp object to real object outside(a), here it calls copy constructor(case1) or copy assignment operator(case2)
class Name{...};
Name func(){
// ...
Name local;
return local;
}
case1:
Name outside = func(); // call copy constructor?
case2:
Name outside;
outside = func(); // call copy assignment operator?
If I was right in the first part, what if return value optimizaiton is enabled?
This question made me think, so in that sense it is a good question. I ran the test code below on Visual Studio C++ Express 2010 in "Debug" mode.
#include <iostream>
using namespace std;
class Foo
{
public:
Foo(int arg) { val = arg; cout<<"Constructor (normal): "<<val<<endl; }
Foo(const Foo& ref) { val = ref.val; cout<<"Copy constructor: "<<val<<endl; }
const Foo& operator=(const Foo& rval)
{
cout<<"Assignment operator from object "<<rval.val<<" to object "<<val<<endl;
val = rval.val;
return *this;
}
int val;
};
Foo process() {Foo t(2); return t; }
int main()
{
Foo a(1);
a = process();
system("pause");
return 0;
}
The result:
Constructor (normal): 1
Constructor (normal): 2
Copy constructor: 2
Assignment operator from object 2 to object 1
Press any key to continue . . .
So Case 2 seems to be correct. After the external 'Foo' is constructed the 'Foo' object is constructed inside the function. Then there is another object created with the copy constructor and finally the assignment operator is called to copy the result to object a.
Return value optimization will depend on the compiler and settings. For example, when I build and run in release mode there is no call to the copy constructor, indicating that the temporary object is only for debug mode. (I'm not an expert on this subject.)
Copy elision/return value optimization in C++ allows it to skip calling a copy contructor.
Case 1, you are using copy construction in the return and to build the instance "outside". Either or both of these can be elided.
C++ is allowed to ellide these function calls even if they have measurable side effects - no as-if rule applies.
Case 2 you are using the assignment operator to get your value into the variable "outside", the not-as-if copy elision exception does not apply here. So, you will get an assignment operator call, unless the compiler can safely elide it due to it not having side effects. In the latter case, the compiler is free to if it chooses, but it will by definition be hard for you to tell what happened.
Consider the following code dealing with reference return:
class Test
{
public:
int data;
Test() { data = 0; }
Test(int u) { data = u;}
Test& myfunction ();// function returning reference
Test(Test& b) // copy constructor
{
cout<<"consructor called";
}
void print() { cout << data<<endl; }
};
Test m(97);
Test& Test:: myfunction ()
{
return m;
};
int main()
{ Test a;
Test b;
b=a.myfunction();// why copy constructor not called?
b.print();
m.data=5;
b.print();// if b is a reference of m then why its value not changed?
return 0;
}
i hav two problem
1) is 'b' becomes reference of 'm' through following:
b=a.myfunction();
if so then why its value not changed when value of 'm' is changed in
m.data=5;
2) is b a normal object?. if so then why copy constructor is not called when following code is executed
b=a.myfunction();
the above code compiles fine with the following output:
97
5
Test b;
You just constructed the b object, using Test's default constructor. Nothing you do will somehow magically "reconstruct" b. Everything you do with b now is being done on an already-constructed b.
b=a.myfunction();// why copy constructor not called?
The copy constructor isn't called because nothing is being constructed. Since you're assigning something to an already-constructed object, the object's copy assignment operator (operator=) is called.
Now if you want to ensure the copy constructor is called, you should do this instead:
Test b = a.myfunction();
is 'b' becomes reference of 'm'
No. You already declared b to be a Test object. Once an object is declared, nothing you do will change it's type. Objects don't change types once created.
Since myfunction() returns a reference, b's assignment operator uses that as the right-hand-side. It doesn't make b a reference -- it just copies stuff from the thing returned by myfunction(), which just so happens to be a reference to something else.
is b a normal object?
Yes. Well, if I'm honest, I don't know what you mean exactly by "normal object." But in whatever sense you mean it, the answer is sureley yes. It's not a reference, and there's nothing magic about b (or anything else in C++, though it may seem so.)
Object b has already been constructed in the line above, so it will not call copy constructor but the assignment operator.
// Using copy constructor:
Test a;
Test b = a.myfunction();
// Using assignment operator
Test c;
c = a.myfunction();
#include <iostream>
using namespace std;
class t{
private:
int * arr;
public:
t() { arr=new int[1]; arr[0]=1;}
t(int x) {arr=new int[1]; arr[0]=x;}
t(const t &);
~t() {cout<<arr[0]<<" de"<<endl; delete [] arr;}
t & operator=(const t & t1){arr[0]=t1.arr[0];return *this;}
void print(){cout<<arr[0]<<endl;}
};
t::t(const t & t1) {arr=new int[1];arr[0]=t1.arr[0];}
int main(){
t b=5;
cout<<"hello"<<endl;
b.print();
b=3;
b.print();
return 0;
}
Why the result is
hello
5
3 de
3
3 de ?
why "t b=5;" will not call the destructor? how "t b=5" works? does it create a temp object (of class t) using constructor "t(int x)" first, then use copy constructor "t(const t &)" to create b?
if it is the case why it does not call the desctructor for the temp object?
why "t b=5;" will not call the destructor?
When you do this:
t b=5;
you get copy initialization. Semantically, the implicit converting constructor t(int) is called, and then the copy constructor t(const t&) is called to instantiate b. However, the compiler is allowed to elide the copy, which is what is happening in your case. The object is constructed in place, without need for a copy construction. This is why you do not see a destructor call. But your class still needs a copy constructor for that code to compile: copy elision is optional, and whether some code compiles should not depend on whether the compiler is performing an elision or not.
If you had said
t b(5);
then there would be a direct initialization, with no copy elision, and with only one constructor call. Your class would not require a copy constructor in for this code to compile.
Since you don't have an assignment operator taking an int, b = 3; is interpreted as
`b.operator=(t(3));`
This creates a temporary t instance, and destroys it once the assignment returns. That's what prints the first de line. Finally, at the end of main, b goes out of scope, its destructor is called and prints the second line.
Maybe a little trace from your program help you understand what is happing:
int main(){
t b=5; // as you expected, this call the constructor of your object.
cout<<"hello"<<endl;
b.print(); // as you could see, this call print() and print 5
b=3; // this is where the confusion begins. You didn't provide a way
// from your object to make an assigment from an integer, but you
// provide a way to construct an object from integer. So, your
// compiler will construct a temporary object, with 3 as parameter
// and use this object to do this assignment. Once this is a
// temporary object, it will be destructed at the end of this
// operation. That is why you are getting the message: 3 de
b.print(); // print 3 as expected
return 0; // call the destruct from the object that was assigned before
}