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.
Related
The following class outputs when a constructor is called:
class A {
public:
A() {
std::cout << "Default Constructor called at address:" << this << "!\n";
}
A(int val) : val_(val) {
std::cout << "Secondary Constructor called at address:" << this << "\n";
}
A(A&& other) : val_(std::move(other.val_)) {
std::cout << "Move constructor called from " << &other << " to " << this << "\n";
}
A& operator=(A&& other) {
std::cout << "Move assignment called from " << &other << " to " << this << "\n";
val_ = std::move(other.val_);
return *this;
}
private:
int val_;
};
This union allows me to skip the default constructor of A, which is desirable:
union MyUnion {
MyUnion() {}
A a;
};
My goal with main was to move-construct a. Instead it move-assigns the a.
int main(){
MyUnion my_union[2];
std::cout << "Entering for loop:\n";
for (int i = 0; i < 2; ++i){
my_union[i].a = std::move(A(i));
}
return 0;
}
An example output from the above main function is:
Entering for loop:
Secondary Constructor called at address:0x7ffcdd3553a8
Move assignment called from 0x7ffcdd3553a8 to 0x7ffcdd3553b0
Secondary Constructor called at address:0x7ffcdd3553a8
Move assignment called from 0x7ffcdd3553a8 to 0x7ffcdd3553b4
I wanted to move-construct, but I'm instead move-assigning. Move-assigning without move constructing is unsafe in general. The solution I came up with was to use std::construct_at. I also added a destructor. The full program with these modifications is below:
#include <iostream>
#include <memory>
#include <utility>
class A {
public:
A() {
std::cout << "Default Constructor called at address:" << this << "!\n";
}
A(int val) : val_(val) {
std::cout << "Secondary Constructor called at address:" << this << "\n";
}
A(A&& other) : val_(std::move(other.val_)) {
std::cout << "Move constructor called from " << &other << " to " << this << "\n";
}
A& operator=(A&& other) {
std::cout << "Move assignment called from " << &other << " to " << this << "\n";
val_ = std::move(other.val_);
return *this;
}
~A() {
std::cout << "Destructor called at " << this << "\n";
}
private:
int val_;
};
union MyUnion {
MyUnion() {
std::cout << "MyUnion Constructor called!\n";
}
void ConstructAt(A&& other){
std::construct_at(&a, std::forward<A>(other));
}
A a;
~MyUnion(){
std::cout << "MyUnion Destructor called!\n";
a.~A();
}
};
int main(){
MyUnion my_union[2];
std::cout << "\nEntering for loop:\n";
for (int i = 0; i < 2; ++i){
my_union[i].ConstructAt(std::move(A(i)));
}
std::cout << "\nExiting for loop:\n";
return 0;
}
This gives output:
MyUnion Constructor called!
MyUnion Constructor called!
Entering for loop:
Secondary Constructor called at address:0x7ffe38dd4838
Move constructor called from 0x7ffe38dd4838 to 0x7ffe38dd4840
Destructor called at 0x7ffe38dd4838
Secondary Constructor called at address:0x7ffe38dd4838
Move constructor called from 0x7ffe38dd4838 to 0x7ffe38dd4844
Destructor called at 0x7ffe38dd4838
Exiting for loop:
MyUnion Destructor called!
Destructor called at 0x7ffe38dd4844
MyUnion Destructor called!
Destructor called at 0x7ffe38dd4840
This appears to be what I want.
Question
Was this the correct way to call the move constructor? Am I using std::forward correctly? Is this the correct way to handle the destructor of a union?
As I remember, before any call of function, it allocs memory for function result and parameters at stack.
Does that means if I have
T func()
{
T a;
return std::move(a);
}
I will still have copying, because it already was allocated memory for entire T?
I also read at similar questions that
return a;
is same as
return std::move(a);
So, I can't avoid copying to stack? Is rvalue a value at stack?
Will it be good way to use it somewhere:
T a = std::move(func());
So I will avoid copying result of function? Do I still have to create special move constructor and move operator=?
I tried to test it and got:
class Temp
{
public:
Temp()
{
cout << "construct" << endl;
i = 5;
}
~Temp()
{
cout << "destruct" << endl;
}
Temp(const Temp& t)
{
i = t.i;
cout << "copy construct" << endl;
}
Temp operator=(const Temp& t)
{
i = t.i;
cout << "operator =" << endl;
return *this;
}
int i;
};
Temp tempfunc1()
{
Temp t1;
t1.i = 7;
return t1;
}
Temp tempfunc2()
{
Temp t1;
t1.i = 8;
return std::move(t1);
}
int main()
{
Temp t1;
Temp t2;
t2.i = 6;
t1 = t2;
cout << t1.i << endl;
t1.i = 5;
t1 = std::move(t2);
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = tempfunc1();
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = std::move(tempfunc1());
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = tempfunc2();
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = std::move(tempfunc2());
cout << t1.i << endl;
}
with result:
construct
construct
operator =
copy construct
destruct
6
operator =
copy construct
destruct
6
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
7
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
7
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
8
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
8
As if there is nothing from using std::move. Or it works just if there exist speacial constructor and destructor?
Why "t1 = t2" calls both copy constructor and operator=?
Excuse me for such big heap of questions that may be very simple, even after reading much about that, maybe because my bad english, I still need explanations.
Thank you in advance.
I've made some changes to your code which may help you understand and explore how this all works. I added an id element to each Temp object to make it easier to understand which object is which and also changed the signature of the operator= to return a reference rather than an object. First, here is the required include to make it into a complete program:
#include <iostream>
using std::cout;
using std::endl;
Next, here's the class which now includes a std::move constructor (with the &&) and a move = operator:
class Temp
{
int id;
public:
Temp() : id(++serial), i(5)
{
cout << "construct " << id << endl;
}
~Temp()
{
cout << "destruct " << id << endl;
}
Temp(const Temp& t) : id(++serial), i(t.i)
{
cout << "copy construct " << id << " from " << t.id << endl;
}
Temp(Temp &&t) : id(++serial), i(t.i)
{
t.i = 5; // set source to a default state
cout << "move construct " << id << " from " << t.id << endl;
}
Temp &operator=(const Temp& t)
{
i = t.i;
cout << "operator = " << id << " from " << t.id << endl;
return *this;
}
Temp &operator=(Temp&& t)
{
i = t.i;
t.i = 5; // set source to a default state
cout << "move operator = " << id << " from " << t.id << endl;
return *this;
}
int i;
static int serial;
};
int Temp::serial = 0;
Your functions are still the same, but see the comment
Temp tempfunc1()
{
Temp t1;
t1.i = 7;
return t1;
}
Temp tempfunc2()
{
Temp t1;
t1.i = 8;
return std::move(t1); // not necessary to call std::move here
}
I've slightly altered main() to show how this all works:
int main()
{
Temp t1;
Temp t2;
t2.i = 6;
t1 = t2;
cout << t1.i << endl;
t1.i = 5;
t1 = t2;
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = tempfunc1();
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = std::move(tempfunc1());
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = tempfunc2();
cout << t1.i << endl;
cout << "NEXT" << endl;
Temp t3(tempfunc1());
cout << t3.i << endl;
cout << "NEXT" << endl;
Temp t4(t1);
cout << t4.i << endl;
}
Finally here is the output:
construct 1
construct 2
operator = 1 from 2
6
operator = 1 from 2
6
NEXT
construct 3
move operator = 1 from 3
destruct 3
7
NEXT
construct 4
move operator = 1 from 4
destruct 4
7
NEXT
construct 5
move construct 6 from 5
destruct 5
move operator = 1 from 6
destruct 6
8
NEXT
construct 7
7
NEXT
copy construct 8 from 1
8
destruct 8
destruct 7
destruct 2
destruct 1
As you can see, with the fixed operator=, no temporary is created. Also once the move version of operator= is provided, temporary objects (as with those returned by your tempfunc1() and tempfunc2() functions) automatically use move semantics. Your tempfunc2() doesn't really need the std::move call within it. As you can see, that merely creates yet another temporary, so it hurts more than it helps. Finally note the in the creation of t3 that there is only a single object created and no temporary or move constructor is required.
update
It's probably worth noting that a move constructor doesn't really help much in this trivial class, but it can help a lot for classes that use allocated memory or are computationally expensive to create. In those cases, it's useful to remember that there are multiple steps required in a move constructor (or in the move version of operator=). Specifically, you must:
release any resources used by the destination object, then
move the resources from the source object to the destination object, then
set the source object state such that the destructor may be called (for example, if the class had memory allocated, you should set the pointer to nullptr so that the destructor will operate correctly) and then finally,
return *this.
Consider this piece of code and its output:
class test {
int a, b;
public:
test (int a, int b) : a(a), b(b) { cout << "Const" << endl;}
test (const test & t) {
cout << "Copy constructor" << endl;
cout << "Being copied to = " << this << " from " << &t << endl;
if (&t == this) {
cout << "returning" << endl;
return;
}
this->a = t.a;
this->b = 6;//t.b;
}
test operator=(const test & in) {
cout << "Assignment operator" << endl;
this->a = in.a;
this->b = in.b;
cout << "Being written to = " << this << " from "<< &in << endl;
return *this;
}
test get () {
test l = test (3, 3);
cout << "Local return " << &l << endl;
return l;
}
void display () {
cout << a << " " << b << endl;
}
};
int main () {
int i = 5, &ref = i;
test t(1,1), u(2,2);
u = t.get();
cout << "u address" << &u <<endl;
//cout << "u ka = " << &u << endl;
u.display();
}
Output:
Const
Const
Const
Local return 0x7fff680e5ab0
Assignment operator
Being written to = 0x7fff680e5a90 from 0x7fff680e5ab0
Copy constructor
Being copied to = 0x7fff680e5a80 from 0x7fff680e5a90
u address0x7fff680e5a90
3 3
I know the way to return from an assignment is by reference, but I was trying to understand how this works since I am a beginner to C++.
What is the address 0x7fff680e5a80 ? Where is this coming from ? Which object is calling the copy constructor here ? I would expect u (address 0x7fff680e5a90) to call it.
Lets follow your code (I modified your constructor to dump the constructed this).
First you isntantiate t and u.
Const at 0x7fffffffdeb0
Const at 0x7fffffffdea0
Then you call t.get, which initializes l.
Const at 0x7fffffffdec0
You then return l.
Local return 0x7fffffffdec0
The copy constructor that would normally happen at this point is elided.
Assignment operator from l to u
Being written to = 0x7fffffffdea0 from 0x7fffffffdec0
Then you are returning the assignment by value (you should return it by reference), so you copy from u to an output value that isn't stored (0x7fffffffde90 from u)
Copy constructor
Being copied to = 0x7fffffffde90 from 0x7fffffffdea0
And then you print u.
u address0x7fffffffdea0
Inside T.
To get rid of the confusing (and unessisary copy) you should return by reference when you do any assignment operator:
test& operator=(const test & in);
Your assignment operator is incorrect:
test operator=(const test & in) {
You should return by reference:
test &operator=(const test & in) {
The object at 0x7fff680e5a80 is a prvalue temporary object of type test that is returned from your (incorrect) assignment operator and immediately discarded:
test operator=(const test & in) {
cout << "Assignment operator" << endl;
this->a = in.a;
this->b = in.b;
cout << "Being written to = " << this << " from "<< &in << endl;
return *this;
} // ^-- copy constructor is called here
You can check this by taking an rvalue reference to the return value of the assignment operator:
auto &&temp = (u = t.get());
std::cout << "Address of temp: " << &temp << std::endl; // prints 0x7fffffffde90
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.
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