I've noticed some behaviour which I can't understand in parameterized constructors. Given the following program:
#include <iostream>
using namespace std;
class A {
public:
int x;
A() {}
A(int i) : x(i){
cout << "A\n";
}
~A(){
cout << "dA\n";
}
};
int main(){
A p;
p = 3;
cout << p.x << endl;
p = 5;
cout << p.x << endl;
return 0;
}
I get as output:
A
dA
3
A
dA
5
dA
This means that using = triggers the parameterized constructor, destroys the object on which it's called and creates a new object.
I cannot understand this behaviour and I can't find the answer in the standard ( I am sure it is there somewhere, but it may be stated in a sophisticated way). Could someone help me with an explanation?
The phrase you're probably looking for is "implicit conversion".
If you add a copy constructor and an assignment operator, and then give each object a unique ID, it's easier to see where things go:
int counter = 0;
class A {
public:
int id;
A(): id(++counter) {cout << "A(): " << id << "\n";}
A(int i) : id(++counter) {cout << "A(" << i << "): " << id << "\n";}
// Don't copy the id.
// (This isn't used anywhere, but you can't see that it's not used unless it exists.)
A(const A& a) : id(++counter) {cout << "A(" << a.id << "): " << id << "\n";}
// Don't copy the id here either.
A& operator=(const A&a) {cout << id << " = " << a.id << "\n"; return *this;}
~A(){cout << "destroy: " << id << "\n";}
};
int main(){
A p;
cout << "p is " << p.id << "\n";
p = 3;
cout << "p is " << p.id << "\n";
p = 5;
cout << p.id << "\n";
}
Output:
A(): 1
p is 1
A(3): 2
1 = 2
destroy: 2
p is 1
A(5): 3
1 = 3
destroy: 3
1
destroy: 1
As you can see, the parameterized constructor is used to create a temporary object whose value can be assigned to p, and that temporary is destroyed immediately after that.
You can also see that p is alive and well until the very end.
With a statement like
p = 3;
what you're actually doing is
p = A(3);
which really translates to
p.operator=(A(3));
The temporary A object created by A(3) of course needs to be destructed, it is temporary after all.
The object p itself will not be destructed by the assignment.
Related
I am working on an Entity class which needs to provide access to its data members through setter methods, which check that a value is allowed before storing it (checking code not shown). The Select class is one of the types stored and it needs to do some very specific cleaning up when destroyed:
#include<memory>
#include <iostream>
using namespace std;
class Select {
int i, j;
friend class Entity;
public:
Select(int a, int b) : i{a}, j{b} { }
~Select() {
cout << "destroying " << i << j << endl;
}
protected:
Select() { };
};
class Entity {
Select select_;
public:
void select_setter(const Select &select) {
cout << "to be assigned... " << select.i << select.j << endl;
select_ = select;
}
static shared_ptr<Entity> create(const Select &s) {
auto sp = make_shared<Entity>(Entity{});
sp->select_setter(s);
return sp;
}
};
This block demonstrates how I want the Entity type to be used:
int main() {
auto sp = Entity::create({1, 1});
sp->select_setter({2, 2});
sp->select_setter({3, 3});
cout << "the end" << endl;
return 0;
}
Here is the output:
destroying 00
to be assigned... 11
destroying 11
to be assigned... 22
destroying 22
to be assigned... 33
destroying 33
the end
destroying 33
Why is 33 destroyed twice, but 00, 11 and 22 only once?
When you want to analyse such constructor/destructor behaviour, my recommendation is to:
Log all constructors (default constructor, copy constructor, additional constructor)
Log all destructors
Log address of the objects to know who is who...
By adding more outputs, and implementing Select and Entity copy constructors you can get:
Before auto sp = Entity::create({1, 1});
In Select 1 1 ctor 0x7fff85e31b80 // That's Select temporary object {1,1} being created
In Select default ctor 0x7fff85e31b40 // That's Entity attribute being created before Entity ctor is entered below
In Entity ctor 0x7fff85e31b40 // That's Entity ctor for Entity{} you create
In Select default ctor 0x2248028 // That's Entity attribute being created before Entity ctor is entered below
In Entity copy ctor copy 0x7fff85e31b40 to 0x2248028 // That's copying Entity{} object as the shared_ptr attribute
In Entity dtor 0x7fff85e31b40 // That's Entity{} being destroyed
destroying 00 0x7fff85e31b40 // That's Entity{}'s Select attribute being destroyed. Is 00 but could be anything else (not initialized)
to be assigned... 11
destroying 11 0x7fff85e31b80 // That's Select temporary object {1,1} being destroyed
Before sp->select_setter({2, 2});
In Select 2 2 ctor 0x7fff85e31b70 // That's Select temporary object ({2,2})
to be assigned... 22
destroying 22 0x7fff85e31b70 // That's Select temporary object ({2,2}) being destroyed
Before sp->select_setter({3, 3});
In Select 3 3 ctor 0x7fff85e31b80 // That's Select temporary object ({3,3})
to be assigned... 33
destroying 33 0x7fff85e31b80 // That's Select temporary object ({2,2}) being destroyed
the end
In Entity dtor 0x2248028 // That's Entity stored in the shared_ptr object being destroyed
destroying 33 0x2248028
And this perfectly makes sense...
Simply use this code:
#include <memory>
#include <iostream>
using namespace std;
class Select {
int i, j;
friend class Entity;
public:
Select(int a, int b) : i{a}, j{b} { std::cout << "In Select " << a << " " << b << " ctor" << std::hex << "0x" << this << std::endl;}
~Select() {
cout << "destroying " << i << j << std::hex << "0x" << this << endl;
}
Select( const Select& e ) {
i = e.i; j = e.j;
std::cout << "In Select copy ctor copy " << std::hex << "0x" << &e << " to " << std::hex << "0x" << this << std::endl;
}
Select() { std::cout << "In Select default ctor" << std::hex << "0x" << this << std::endl; }
};
class Entity {
Select select_;
public:
Entity() { std::cout << "In Entity ctor " << std::hex << "0x" << this << std::endl; }
Entity( const Entity& e ) {
select_ = e.select_;
std::cout << "In Entity copy ctor copy " << std::hex << "0x" << &e << " to " << std::hex << "0x" << this << std::endl; }
~Entity() { std::cout << "In Entity dtor " << std::hex << "0x" << this << std::endl; }
void select_setter(const Select &select) {
cout << "to be assigned... " << select.i << select.j << endl;
select_ = select;
}
static shared_ptr<Entity> create(const Select &s) {
auto sp = make_shared<Entity>(Entity{});
sp->select_setter(s);
return sp;
}
};
int main() {
std::cout << "Before auto sp = Entity::create({1, 1});" << std::endl;
auto sp = Entity::create({1, 1});
std::cout << "Before sp->select_setter({2, 2});" << std::endl;
sp->select_setter({2, 2});
std::cout << "Before sp->select_setter({3, 3});" << std::endl;
sp->select_setter({3, 3});
cout << "the end" << endl;
return 0;
}
I cannot understand the order of constructor and destructor calls? What will execute first in this statement A b=f(a)? Can someone please help me out?
#include<iostream>
using namespace std;
class A {
int x;
public:
A(int val = 0)
:x(val) {
cout << "A " << x << endl << flush;
}
A(const A& a) {
x = a.x;
cout << "B " << x << endl << flush;
}
void SetX(int x) {
this->x = x;
}
~A() {
cout << "D " << x << endl << flush;
}
};
A f(A a) {
cout << " C " << endl << flush;
a.SetX(100);
return a;
}
int main()
{
A a(1);
A b=f(a);
b.SetX(-100);
return 0;
}
Output Window:
A 1
B 1
C
B 100
D 100
D -100
D 1
Why does it print B 1 in line 2 of the output window?
"Why does it print B 1 in line 2?"
Because the copy constructor was called from this statement
A b=f(a);
The function f() requires A being passed by value, thus a copy for this parameter is made on the function call stack.
If your next question should be, how you can get over this behavior, and avoid to call the copy constructor, you can simply pass the A instance as a reference to f():
A& f(A& a) {
// ^ ^
cout << " C " << endl << flush;
a.SetX(100);
return a;
}
Side note: endl << flush; is redundant BTW, std::endl includes flushing already.
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 the following class:
struct MyClass {
int mId;
MyClass(int id): mId(id) {}
~MyClass() { std::cout << "deleting: " << mId << std::endl; }
};
And the usage:
std::shared_ptr<MyClass> p(new MyClass(0));
MyClass& m = *p;
m = MyClass(2);
The result is:
deleting: 2
deleting: 2
Please help me understanding:
Why there are two MyClass(2) objects (assumption made on the destructor logs)
Is this a memory leak? Shouldn't the MyClass(0) leak?
Thank you.
There is no memory leak. This code:
m = MyClass(2);
creates a temporary object of type MyClass which is copied into m using the (default-generated) copy assignment operator of MyClass, and then destructed. Eventually, p runs out of scope and its destructor destroys the MyClass instance to which is points (the one bound to m).
If we spelled out all the implicit calls explicitly, this is happening:
// std::shared_ptr<MyClass> p(new MyClass(0));
tmp1.MyClass(0);
p.shared_ptr(&tmp1);
// MyClass& m = *p;
p.operator* ();
// m = MyClass(2);
tmp2.MyClass(2);
m.operator= (tmp2);
tmp2.~MyClass();
// p goes out of scope
p.~shared_ptr();
tmp1.~MyClass();
Do this for a more clear picture of creation destruction:
struct MyClass {
int mId;
MyClass(int id): mId(id) {std::cout << "Creating: " << this << "(" << mId << ")\n";}
~MyClass() {std::cout << "Deleting: " << this << "(" << mId << ")\n";}
MyClass(MyClass const& c)
{std::cout << "Copy: " << this << "(" << mId << ")\n"
" From: " << &c << "(" << c.mId << ")\n";
mId=c.mId;
}
MyClass& operator=(MyClass const& c)
{std::cout << "Assign: " << this << "(" << mId << ")\n"
" From: " << &c << "(" << c.mId << ")\n";
mId=c.mId;
}
};
When I run I get:
Creating: 0x7fc741c000e0(0)
Creating: 0x7fff50ac38c0(2)
Assign: 0x7fc741c000e0(0)
From: 0x7fff50ac38c0(2)
Deleting: 0x7fff50ac38c0(2)
Deleting: 0x7fc741c000e0(2)
I want to write a library wrapper class (LibWrap) around a C library that uses malloc/free. To do this I want to use C++'s RAII to allocate and free memory. I used lib_address as a random example address that I would receive from the library. However when defining my memeber variable the destructor that is called somehow has this lib_address.
I would expect the destructor of the member variable created by the default constructor not to know the new address that I am putting into the constructor of my replacement member variable.
#include <stdlib.h>
#include <iostream>
using namespace std;
class LibWrap
{
int j;
int lib_address;
public:
LibWrap(): //default LibWrap
j(0),
lib_address(0)
{
cout << "default LibWrap "<<j <<"\t\t"<<lib_address << "\t" << this<<endl;
}
LibWrap(int f_j): //special LibWrap
j(0),
lib_address(0)
{
j = f_j;
lib_address = rand();
cout << "special LibWrap " << j<<"\t"<< lib_address<< "\t" << this <<endl;
}
~LibWrap()
{
cout << "killing LibWrap " << j<<"\t" <<lib_address <<"\t" << this<< endl;
}
int g()
{
return j;
}
};
class A
{
int i;
LibWrap b;
public:
A(): //default A
i(0)
{
cout << "default A\t"<<i << endl;
}
A(int f_i)://special A
i(0)
{
i = f_i;
cout << "special A\t"<<i << endl;
b = LibWrap(10);
}
~A()
{
cout << "killing A\t"<<i << endl;
}
void p()
{
cout <<"Test values: "<< i<< "," << b.g() << endl;
}
};
int f()
{
//A a; a.p();
cout << "variable\t\tlib_address\treal_address" << endl;
A a = A(1);
cout << "End" << endl;
//a.p();
}
int main()
{
f();
}
Running this code I would expect to get the following result:
variable lib_address real_address
default LibWrap 0 0 0xbfef2e28
special A 1
special LibWrap 10 1804289383 0xbfef2df8
killing LibWrap 10 1804289383 0xbfef2df8 --would expect kiling LibWrap 0 0 0xbfef2e28
End
killing A 1
killing LibWrap 10 1804289383 0xbfef2e28 --would expect killing LibWrap 10 1804289383 0xbfef2df8
b = LibWrap(10);
This does not initialize b. b has already been initialized as part of constructing A. What you're doing is creating a temporary LibWrap and copying that temporary into b. Then, you're destroying that temporary LibWrap (which is where the extra destructor call with the lib_address comes from).
The temporary LibWrap is where the "0xbfef2df8" address comes from. The b variable is the "0xbfef2e28" address. That's why you're getting them in that order.