#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
}
Related
#include <iostream>
#include<vector>
using namespace std;
class test{
public:
test(){
cout<<"constructor called"<<endl;
}
test(const test& obj){
cout<<"copy constructor called"<<endl;
}
test(test&& obj){
cout<<"Move constructor called"<<endl;
}
};
int main()
{
vector<test> vec;
vec.emplace_back(test());
return 0;
}
When i run above program I expected emplace_back to create object in vector in place.Thus "constructor called" should have been output since emplace_back would avoid creating temporary object.
But the output is:
constructor called
Move constructor called
Here, temporary object is created just like push_back. Please explain.
emplace_back doesn't construct temporaries, but you constructed a temporary object explicitly by test(), then the new element is added to the vector from the temporary by the move constructor.
You can just
vec.emplace_back();
With emplace_back(test()) you already created an object outside of emplace_back and it has a move constructor so it is move-constructed. So you should call it without any argument for this case. Then you will not see any copy/move constructor calls.
vec.emplace_back(); // Will create a test object with constructor `test()` internally
To further understand, if your test class have more constructors, you can give emplace_back with those constructors. For example,
class test {
...
test(int a, int b);
test(const char* c);
};
And you can do this.
vec.emplace_back(1, 2);
vec.emplace_back("abcd");
This does not create redundant object which is cannot be done with push_back.
With emplace_back, the method already knows what class type that you're adding to your vector (you name it when initialising the vector), so the input arguments for emplace_back is only the arguments for the constructor that you want to call (typically you want to avoid the copy constructor, whose argument is an object of the same class):
struct A
{
A (int a, int b, int c)
{
// do something
}
A (const A & other)
{
//do something else
}
};
std::vector<A> array;
array . emplace_back (1, 2, 3);
// the above finds the constructor with these input arguments
// makes the new object within the vector - no copy
A obj (4, 5, 6);
array . emplace_back ( obj );
// the above looks for the constructor with this object (A)
// it finds a constructor (the copy constructor) and copies
array . emplace_back ( A (1,2,3) );
// the above first processes the inner part: making a new A object
// then searches for a constructor with that argument (an object A)
// in this case that's the copy constructor
In your case you were wanting to call a constructor with no arguments. This is why you want to use emplace_back() with no arguments to use this vector method correctly.
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.
I was going through Thinking in C++ and have some confusion regarding the behaviors of constructors in C++. Here is my sample code:
#include<iostream>
using namespace std;
class base
{
public:
int a;
/*Ctor 1*/ base() { cout<<" default"<<endl; }
/*Ctor 2*/ base(int a){ cout<<" base::int "<<endl; }
/*Ctor 3*/ base(const base& b) { cout<<" base::cc "<<endl; }
/*Asgn 1*/ base operator=(base b){
cout<<" base::assignment - base"<<endl;
return b;
}
/*Asgn 2*/ /* base operator=(int b){
cout<<" base::assignment - int"<<endl;
return (base)b;
} */
};
int main()
{
base b;
int a = 10;
b = a;
system("PAUSE");
return 0;
}
Output :
Could anyone please explain me the output ?
I expected just a call to
Default constructor.
Parameterized constructor.
I am unable to understand why I get a call to assignment operator and copy constructor other object being "int" type. If I uncomment "Asgn 2' I get a call to it rather that Asgn 1 which is understandable.
If I am getting a call to copy constructor (which always take object reference as its parameter), is it because compiler casts int to base type?
The output
default
base::int
base::assignment - base
base::cc
Comes about as follows:
base b;
Here create a b - This will use the default constructor
int a = 10;
b = a;
We have an assignment - the only one available takes a value of type base - so the compiler scratches its head and say "ah-ha" got a version of a constructor that can create an object of type base from an int. We can use that.
So you get the output
cout<<" base::int "<<endl;
Now the compiler can use the assignment operator. The parameter is an object of type base but as it is temporary this does not need to be called (see http://en.cppreference.com/w/cpp/language/copy_elision), The assignment operator then outputs
cout<<" base::assignment - base"<<endl;
But the assignment returns the value not as a reference - so it need to copy this return value into b - thus calling the copy constructor. Hence
cout<<" base::cc "<<endl;
First of all, base(int a) is a converting constructor as
it takes one argument
it doesn't use the explicit keyword
A converting constructor can be used for implicit conversions:
void foo(base b);
void bar() {
foo(3);
}
Here the int argument will be implicitly converted to base type with the converting constructor.
Because by value arguments (arguments passed by reference) are copied, the copy constructor is officially called; but here, the source of the copy is a temporary object, the implicitly created object using the int constructor. So the compiler is allowed to fuse the temporary and the parameter, directly constructing the target object. This optimisation is optional, and the compiler must still verify that the copy constructor could be called: that it is declared and accessible here (public).
Because the optimisation is very simple, almost all (or all?) compilers do it; many compilers do it at even the less aggressive optimisation levels (where most optimisations are disabled).
You declare the assignment operator as taking by a parameter by value and returning a copy (not a reference), which is quite rare (but not illegal):
/*Asgn 1*/ base operator=(base b){
cout<<" base::assignment - base"<<endl;
return b;
}
This means that the copy constructor is needed to pass an argument to the assignment operator, and also for return instruction.
Please note that the fact it is an operator is irrelevant, you could have called it assign:
/*Asgn 1*/ base assign(base b){
cout<<" base::assignment - base"<<endl;
return b;
}
and call it normally:
base a,b;
a.assign(b);
b.assign(base());
b.assign(base(2));
b.assign(3);
a.assign(b) will call the copy constructor to create the parameter of assign.
base() creates a temporary object using the default constructor, and base(2) creates one using the int constructor (when you explicitly create a temporary, it does not matter whether the constructor is a converting constructor). Then you can assign on the created temporary. The copy construction is avoided by the compiler by constructing directly the parameter.
In b.assign(3), the creation of the temporary is implicit, and the fact the constructor is a converting constructor is relevant.
The return statement creates another copy; the usual idiom for operator= is:
type& type::operator= (const type &source) {
copy stuff
return *this;
}
A reference is bound to the target object, and no redundant copying occurs.
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();
I'm confused about when a move constructor gets called vs a copy constructor.
I've read the following sources:
Move constructor is not getting called in C++0x
Move semantics and rvalue references in C++11
msdn
All of these sources are either overcomplicated(I just want a simple example) or only show how to write a move constructor, but not how to call it. Ive written a simple problem to be more specific:
const class noConstruct{}NoConstruct;
class a
{
private:
int *Array;
public:
a();
a(noConstruct);
a(const a&);
a& operator=(const a&);
a(a&&);
a& operator=(a&&);
~a();
};
a::a()
{
Array=new int[5]{1,2,3,4,5};
}
a::a(noConstruct Parameter)
{
Array=nullptr;
}
a::a(const a& Old): Array(Old.Array)
{
}
a& a::operator=(const a&Old)
{
delete[] Array;
Array=new int[5];
for (int i=0;i!=5;i++)
{
Array[i]=Old.Array[i];
}
return *this;
}
a::a(a&&Old)
{
Array=Old.Array;
Old.Array=nullptr;
}
a& a::operator=(a&&Old)
{
Array=Old.Array;
Old.Array=nullptr;
return *this;
}
a::~a()
{
delete[] Array;
}
int main()
{
a A(NoConstruct),B(NoConstruct),C;
A=C;
B=C;
}
currently A,B,and C all have different pointer values. I would like A to have a new pointer, B to have C's old pointer, and C to have a null pointer.
somewhat off topic, but If one could suggest a documentation where i could learn about these new features in detail i would be grateful and would probably not need to ask many more questions.
A move constructor is called:
when an object initializer is std::move(something)
when an object initializer is std::forward<T>(something) and T is not an lvalue reference type (useful in template programming for "perfect forwarding")
when an object initializer is a temporary and the compiler doesn't eliminate the copy/move entirely
when returning a function-local class object by value and the compiler doesn't eliminate the copy/move entirely
when throwing a function-local class object and the compiler doesn't eliminate the copy/move entirely
This is not a complete list. Note that an "object initializer" can be a function argument, if the parameter has a class type (not reference).
a RetByValue() {
a obj;
return obj; // Might call move ctor, or no ctor.
}
void TakeByValue(a);
int main() {
a a1;
a a2 = a1; // copy ctor
a a3 = std::move(a1); // move ctor
TakeByValue(std::move(a2)); // Might call move ctor, or no ctor.
a a4 = RetByValue(); // Might call move ctor, or no ctor.
a1 = RetByValue(); // Calls move assignment, a::operator=(a&&)
}
First of all, your copy constructor is broken. Both the copied from and copied to objects will point to the same Array and will both try to delete[] it when they go out of scope, resulting in undefined behavior. To fix it, make a copy of the array.
a::a(const a& Old): Array(new int[5])
{
for( size_t i = 0; i < 5; ++i ) {
Array[i] = Old.Array[i];
}
}
Now, move assignment is not being performed as you want it to be, because both assignment statements are assigning from lvalues, instead of using rvalues. For moves to be performed, you must be moving from an rvalue, or it must be a context where an lvalue can be considered to be an rvalue (such as the return statement of a function).
To get the desired effect use std::move to create an rvalue reference.
A=C; // A will now contain a copy of C
B=std::move(C); // Calls the move assignment operator
Remember that copy elision could occur. If you disable it by passing the -fno-elide-constructors flag to the compiler your constructor might get executed.
You can read about it here: https://www.geeksforgeeks.org/copy-elision-in-c/
Answers above do not give a 'natural' example when a move constructor is called. I found this way to call move constructor without std::move (and without suppressing copy elision by -fno-elide-constructors):
a foo(a a0) {
return a0; // move ctor is called
}
a a1 = foo(a());