Understanding C++ heap/stack allocations when passing dereferenced pointer by reference - c++

I am trying to understand what happens with object creation and destruction in this example.
#include <iostream>
class Person {
public:
const int name;
Person() = delete;
Person(int& name) : name(name) {
std::cout << "Person " << name << " created" << std::endl;
}
Person(const int& name) : name(name) {
std::cout << "Person " << name << " created -- copy constructor" << std::endl;
};
~Person() {
std::cout << "Person " << name << " destroyed" << std::endl;
}
};
class Family {
public:
Person mom, dad;
Family() = delete;
Family(Person& m, Person& d) : mom(m), dad(d) {
std::cout << "Family created" << std::endl;
};
Family(const Person& m, const Person& d) : mom(m), dad(d) {
std::cout << "Family created -- copy constructor" << std::endl;
};
~Family() {
std::cout << "Family destroyed" << std::endl;
}
};
int main()
{
Person* m = new Person(1);
Person* d = new Person(2);
Family f(*m, *d);
return 0;
}
This outputs
Person 1 created -- copy constructor
Person 2 created -- copy constructor
Family created
Family destroyed
Person 2 destroyed
Person 1 destroyed
So I am not entirely sure how to interpret this. I've been taught that anything I heap-allocate using the new keyword should subsequently be delete-d. And stack variables are lost when the object moves out of scope. My understanding is like this. If instead of accepting Person& m by reference I took it without the &, like Person m, then m would be copied here (on the stack) and I'd have a memory leak because the heap-allocated new Person("Jane") would never be deleted.
But since I am taking it by reference, do I still have a memory leak? Both m and d have their destructors called, but does this also free the heap-memory? And how can I think of Family.dad? Is this a stack variable? If so, then is the entire Family considered a scope?
I am quite confused by this example and don't really know how to reason about it. Also, do I still have a memory leak here since I never explicitly delete the two Person objects?

Add copy-constructor to see the whole picture.
Although Family::Family gets the objects by reference, it then copies them into the members mom and dad. So the destructor invocations you observe actually occur when those members get destructed.
The original objects are not freed - until the program exits.
As for the location of Person instances you create using new - they are allocated in the "free store", as per C++ Standard. Typically, it means they reside on the heap.
OTOH, Family instance (along with all its members) has an "automatic storage". With most of popular compilers/platforms, this means it's allocated on the stack.

Related

c++ - Destructor called on assignment

I'm still learning the basics of c++ so I may not have had the correct vocabulary to find the answer to my question but I couldn't find this mentioned anywhere.
If I have a class with a constructor and destructor why does the destructor get called on the new data when I am assigning to the class?
For example:
#include <iostream>
class TestClass {
public:
int* some_data;
TestClass() {
std::cout << "Creating" << std::endl;
some_data = (int*)malloc(10*sizeof(int));
}
~TestClass() {
std::cout << "Deconstructing" << std::endl;
free(some_data);
}
TestClass(const TestClass& t) : some_data{t.some_data} {
std::cout << "Copy" << std::endl;
}
};
int main() {
TestClass foo;
std::cout << "Created once" << std::endl;
foo = TestClass();
std::cout << "Created twice" << std::endl;
}
which prints:
Creating
Created once
Creating
Deconstructing
Created twice
Deconstructing
free(): double free detected in tcache 2
Aborted (core dumped)
So after following this in the debugger it appears the deconstructor is called on the newly created data which is confusing to me. Shouldn't the original data be freed once and then at the end of execution the new data should be freed? It seems like the original data is never freed like this.
Your object owns a raw pointer to allocated memory, but does not implement a proper copy constructor that makes an allocation and copies the data behind the pointer. As written, when you copy an object, the pointer is copied, such that now two objects point to the same address (and the old one that the just-assigned-to object is leaked.)
When the temporary goes out of scope, it deletes its pointer but the copy (foo) still points to it. When foo goes out of scope, it deletes the same pointer again, causing this double free error you're seeing.
If you need to write a destructor to clean up, you almost always need to also provide copy and assignment operations, or disable them.
SUGGESTIONS:
hold the pointer in a std::unique_ptr which will fail to compile if you try to copy it. This forces you to deal with the issue. Also, malloc and free are mainly for C or low-level C++ memory management. Consider using new and delete for allocations instead. (unique_ptr uses delete by default, not free, and you must not mix them.)
alternately, delete the copy constructor and assignment operator
also, consider when you want to move from an xvalue (temporary or moved lvalue), you can pilfer the allocation from the right-hand-side. So this class is a good candidate for move constructor and move assignment.
Most of the comments and a few details more in code:
#include <iostream>
#include <array>
#include <memory>
class TestClass
{
// members of a class should not be public
private:
// TestClass owns the data, this is best modeled
// with a unique_ptr. std::array is a nicer way of
// working with arrays as objects (with size!)
std::unique_ptr<std::array<int, 10>> some_data;
public:
TestClass() :
some_data{ std::make_unique<std::array<int,10>>() }
{
std::cout << "Creating" << std::endl;
// set everything in the array to 0
std::fill(some_data->begin(), some_data->end(), 0);
}
~TestClass()
{
std::cout << "Destructing" << std::endl;
// no need to manually delete a std::unique_ptr
// its destructor will free the memory
// and that will be called as part of this destructor
}
TestClass(const TestClass& t) :
// when you copy a class the copy should have its
// own copy of the data (to avoid deleting some data twice)
// or you must chose shared ownership (lookup std::shared_ptr)
some_data{ std::make_unique<std::array<int,10>>() }
{
std::cout << "Copy" << std::endl;
// copy data from t to this instances array
// (note this would not have been necessary
// it your array was just a non-pointer member,
// try that for yourself too.)
std::copy(some_data->begin(), some_data->end(), t.some_data->begin());
}
TestClass(TestClass&& rhs) :
some_data{ std::move(rhs.some_data) } // transfer ownership of the unique_pointer to this copy
{
std::cout << "Move" << std::endl;
}
// Important this assignement operator is used in your original code too
// but you couldn't see it!
TestClass& operator=(const TestClass& t)
{
some_data = std::make_unique<std::array<int, 10>>();
std::copy(some_data->begin(), some_data->end(), t.some_data->begin());
std::cout << "Assignment" << std::endl;
return *this;
}
};
int main()
{
TestClass foo;
std::cout << "Created once" << std::endl;
foo = TestClass();
std::cout << "Created twice" << std::endl;
TestClass bar{ std::move(foo) };
std::cout << "Moved" << std::endl;
}

does std::vector copy/move elements when re-sizing?

I was messing around with move c'tors for a learning / refresh exercise and I came across something unexpected to me. Below I have a class person that contains a std::string m_name;. I am using this as a test class for copy/move c'tors.
Here is the code for quick reference:
#include <iostream>
#include <vector>
class person
{
public:
std::string m_name;
explicit person(const std::string &name) : m_name(name)
{
std::cout << "created " << m_name << std::endl;
}
~person()
{
std::cout << "destroyed " << m_name << std::endl;
}
person(const person &other) : m_name(other.m_name)
{
m_name += ".copied";
std::cout << "copied " << other.m_name << " -> " << m_name << std::endl;
}
person(const person &&other) noexcept : m_name(std::move(other.m_name))
{
m_name += ".moved";
std::cout << "moved " << other.m_name << " -> " << m_name << std::endl;
}
};
int main()
{
std::vector<person> people;
people.reserve(10);
std::cout << "\ncopy bob (lvalue):" << std::endl;
person bob{"bob"};
people.push_back(bob);
std::cout << "\nmove fred (lvalue):" << std::endl;
person fred{"fred"};
people.push_back(std::move(fred));
std::cout << "\ntemp joe (rvalue):" << std::endl;
people.push_back(person{"joe"});
std::cout << "\nterminating:" << std::endl;
}
This gives me the output that I would expect (mostly, except why std::string contents is not "moved"?): https://godbolt.org/z/-J_56i
Then I remove the std::vector reserve so that std::vector has to "grow" as I am adding elements. Now I get something that I really don't expect: https://godbolt.org/z/rS6-mj
Now I can see that bob is copied and then moved when fred is added and then moved again when joe is added. I was under the impression that std::vector "moved" when it has to reallocate space. But I thought that it did a memory copy/move, not an object-by-object copy/move. I really did not expect it to call the move constructor.
Now if I remove the move c'tor, I find that bob is copied three times!: https://godbolt.org/z/_BxnvU
This seems really inefficient.
From cplusplus.com:
push_back()
Add element at the end Adds a new element at the end of the vector,
after its current last element. The content of val is copied (or
moved) to the new element.
This effectively increases the container size by one, which causes an
automatic reallocation of the allocated storage space if -and only if-
the new vector size surpasses the current vector capacity.
resize()
Resizes the container so that it contains n elements.
If n is smaller than the current container size, the content is
reduced to its first n elements, removing those beyond (and destroying
them).
If n is greater than the current container size, the content is
expanded by inserting at the end as many elements as needed to reach a
size of n. If val is specified, the new elements are initialized as
copies of val, otherwise, they are value-initialized.
If n is also greater than the current container capacity, an automatic
reallocation of the allocated storage space takes place.
Notice that this function changes the actual content of the container
by inserting or erasing elements from it.
I guess it does not really describe "how" it does the reallocation, but surely a memory copy is the fastest way to move the vector to its newly allocated memory space?
So why are the copy/move c'tors called when std::vector is added to instead of a memory copy?
A side note/question: (any maybe this should be a separate question): In person move c'tor why is moved fred -> fred.moved printed and not moved -> fred.moved. It appears that the std::string move assignment does not really "move" the data...
If it needs to relocate, something similar to std::move(xold.begin(), xold.end(), xnew.begin()); will be used. It depends on the value type and the vector usually does its own internal placement new . but it'll move if it can move.
Your move constructor
person(const person &&other) noexcept;
has a flaw though: other should not be const since it must be allowed to change other to steal its resources. In this move constructor
person(person&& other) noexcept : m_name(std::move(other.m_name)) {}
the std::strings own move constructor will do something similar to this:
string(string&& other) noexcept :
the_size(other.the_size),
data_ptr(std::exchange(other.data_ptr, nullptr))
{}
You also need to add a move assignment operator:
person& operator=(person &&other) noexcept;

Copy constructor and dynamic memory

I am a beginner in programming and I am learning about copy constructors. From different sources I can see that copy constructors are useful if I want to "deep copy" a class object so the new object's pointer members will point to new memory locations.
My question is, what is the advantage of defining the copy constructor as I do in class CopyCat in my example, if I get the same result with an empty copy constructor (as in class EmptyCat)?
My second question is, why do class Cat and class EmptyCat work differently? The only difference between them is that I define an empty copy constructor in EmptyCat. But as I run the program I can see that in EmptyCat after the copying the pointer member points to a new location while in class Cat it works as a shallow copy.
#include "iostream"
class Cat
{
public:
void GetMem() { std::cout << itsAge << "\n"; }
private:
int * itsAge = new int;
};
class EmptyCat
{
public:
EmptyCat() {}
~EmptyCat() {}
EmptyCat(EmptyCat&obj) {}
void GetMem() { std::cout << itsAge << "\n"; }
private:
int * itsAge = new int;
};
class CopyCat
{
public:
CopyCat() {}
~CopyCat() {}
CopyCat(CopyCat&obj);
int GetAge() { return *itsAge; }
void GetMem() { std::cout << itsAge << "\n"; }
private:
int * itsAge = new int;
};
CopyCat::CopyCat(CopyCat & obj)
{
itsAge = new int;
*itsAge = obj.GetAge();
}
int main()
{
Cat Garfield;
Cat Kitty(Garfield);
std::cout << "Memory addresses for the objects' <itsAge> member:" << std::endl;
std::cout << "Garfield and Kitty (Class Cat):" << std::endl;
Garfield.GetMem();
Kitty.GetMem();
EmptyCat Meow;
EmptyCat Purr(Meow);
std::cout << std::endl << "Meow and Purr (Class EmptyCat):" << std::endl;
Meow.GetMem();
Purr.GetMem();
CopyCat Fluffy;
CopyCat Felix(Fluffy);
std::cout << std::endl << "Fluffy and Felix (Class CopyCat):" << std::endl;
Fluffy.GetMem();
Felix.GetMem();
system("pause");
return 0;
}
If I run the program I get this:
Memory addresses for the objects' <itsAge> member:
Garfield and Kitty (Class Cat):
00BBDA60
00BBDA60
Meow and Purr (Class EmptyCat):
00BB46A0
00BB8280
Fluffy and Felix (Class CopyCat):
00BB82B0
00BBE8A0
Press any key to continue . . .
Deep copying and shallow copying is rather a C concept, where you have only structures and raw pointers. A pointer can be owned, in which case the copy must be deep, or it can be shared, in which case the copy is shallow (and you have to be careful about freeing it if it's allocated with malloc).
In C++, new is now effectively deprecated. We have unique pointers, which are "owning pointers" and "shared pointers". However pointers are relatively rare. Array members of classes are std::vectors, string members are std::strings. And copies are automatically deep, (You use a reference if you want a shallow copy).
Pointers are held back for relatively unusual situations, like trees and graphs.
My question is, what is the advantage of defining the copy constructor as I do in class CopyCat in my example, if I get the same result with an empty copy constructor (as in class EmptyCat)?
You don't get the same result. CopyCat allocates new memory and copies the value from the old class. The EmptyCat just allocates new memory, but does not copy the value.
My second question is, why do class Cat and class EmptyCat work differently? The only difference between them is that I define an empty copy constructor in EmptyCat. But as I run the program I can see that in EmptyCat after the copying the pointer member points to a new location while in class Cat it works as a shallow copy.
In Cat you haven't declared a copy constructor, so the compiler will generate one when needed. The default copy constructor does a member-wise copy from the original. In your case, this will copy the pointer (so that it stores the same address as the original).
In the EmptyCat you have a user defined copy constructor. But as that one doesn't handle the pointer member, its default value will be used.
int * itsAge = new int;
This is what allocates a new int and gets you a different pointer value.
You are not getting the same behavior with and without an empty copy constructor. EmptyCat(EmptyCat& obj) { } does absolutely nothing.
CopyCat(CopyCat& obj) {
itsAge = new int;
*itsAge = obj.GetAge();
}
dynamically allocates a new int and assigns to it a value from the obj.

Why is there no memory leak here

I was trying to answer this question, so I decided to create the following simple test case so that the OP could see by himself the memory leak.
#include<iostream>
class MyObject
{
public:
MyObject(){std::cout << "creation of my object" << std::endl;}
virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
};
void processMyObject(MyObject foo)
{
}
int main()
{
processMyObject(*new MyObject());
return 0;
}
I compiled it :
g++ test.cpp -o test
And then, I saw an unexpected output :
creation of my object
destruction of my object
I have absolutly no idea of what is happening here. Could anyone explain to me ?
PS: I used g++ 4.6.3
Since you pass an object by value to the function, you incurr a copy or move-copy construction. But you are not keeping track of that with your primitive memory leak checker. You could provide your own copy constructor, and then you will see two objects are being created, and only one is being destroyed:
#include<iostream>
class MyObject
{
public:
MyObject() {std::cout << "creation of my object" << std::endl;}
MyObject(const MyObject&) {std::cout << "copy creation of my object" << std::endl;}
~MyObject() {std::cout << "destruction of my object" << std::endl;}
};
void processMyObject(MyObject foo) {}
int main()
{
processMyObject(*new MyObject());
}
Output:
creation of my object
copy creation of my object
destruction of my object
Because you're taking the MyObject by value.
Thus there is a destruction. But it is the destruction of the foo argument at the end of processMyObject.
The *new does actually still leak in this case.
EDIT: As pointed out by juanchopanza, you need to also print a statement in the copy constructor and in the move constructor as well.
What happens in your code
You construct an object and get information about it (creation of my object)
You pass it to function - copy constructor fires, but does not report anything
The copy is destroyed - you get information about it (destruction of my object)
The original instance leaks despite fact, that you don't have any information about it.
How to see it?
Simply report pointers to this during construction and destruction (quick'n'dirty, please don't complain):
class MyObject
{
public:
MyObject(){std::cout << "creation of my object (" << (int)this << ")" << std::endl;}
virtual ~MyObject(){std::cout << "destruction of my object (" << (int)this << ")" << std::endl;}
};
Result:
creation of my object (165437448)
destruction of my object (-1076708692)
As you see, destroyed object is different than created one.
How to "fix" it to show the leak?
The simplest way to "fix" your code is to pass object by pointer:
#include<iostream>
class MyObject
{
public:
MyObject(){std::cout << "creation of my object" << std::endl;}
virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
};
void processMyObject(MyObject * foo)
{
}
int main()
{
processMyObject(new MyObject());
return 0;
}
Another option is to report copy ctors and move ctors as well:
class MyObject
{
public:
MyObject(){std::cout << "creation of my object" << std::endl;}
MyObject(const MyObject & obj) { std::cout << "copy-ctor" << std::endl; }
MyObject(MyObject && obj) { std::cout << "move-ctor" << std::endl; }
virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
};
There actually is a memory leak. Just because your object was destroyed, doesn't mean that you deleted the resources you acquired with new. You have to explicitly use delete.
EDIT
So here's what's happening:
You are calling the default constructor on the fly, and passing it to the function call. This prints the first message.
As part of the function call, the object is passing to processMyObject(), creating a new object in that scope using the copy constructor, which has been implicitly defined by the compiler.
When that object (in processMyObject()) goes out of scope, its destructor is called, printing the second message.
So, the instance printing the first message is different from that printing the second one.
Hope that clears things up.

Why when using in const& in the assignment disructor called , but assignment is correct [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
How to return member that can be changed?
I learn that if i use in const& in the assignment (and in the called method signature) than the lifetime of the refereed object is extended until end of method.
Employee const& getEmp(int a) {
return Employee(a);
}
Employee const& tmpEmp = m.getEmp(10); //
... stuff
//end of scope - tmpEmp valid until here
I wrote little program and saw that it work as expected.
My question is not how to do this?
My question is about how compiler do this?
As you see from the example the destructor is called immediately after return , so i wonder how is that the destructor called , but tmpEmp is valid after the desructor called ?
#include <iostream>
using namespace std;
class Employee {
public:
Employee() : a(0){
cout << "c-emp" << a << endl;
}
Employee(const Employee& newE) {
a = newE.a;
cout << "c-c-emp" << a << endl;
}
Employee(int a) : a(a) {
cout << "c-emp" << a << endl;
}
~Employee() {
cout << "d-emp" << a << endl;
}
int a;
};
class Manager {
public:
Manager() {}
~Manager() {}
Employee const& getEmp(int a) {
return Employee(a);
}
};
int main(int argc, char **argv) {
Manager m;
Employee const& tmpEmp = m.getEmp(10);
cout << "tmpEmp " << tmpEmp.a <<endl;
}
output:
c-emp10
d-emp10 - destructor is called and tmpEmp is still valid? how is that?
tmpEmp 10
You learned wrong.
First, assignment never has any effect on the lifetime of an object.
It's just an operator with a side effect.
Second, if you initialize (not assign) a const reference with a
temporary, the lifetime of the temporary is extended to match the
lifetime of the reference. There are exceptions, however. (And I've
never found any pratical use for this feature.)
A const reference used as a return value is one of the exceptions (for
the simple reason that it's not implementable). Initializing a return
of a const reference type with a temporary does not extend the life of
the temporary.
And finally, even if it did, it wouldn't help you in your case, because
the reference which was initialized by the temporary ceases to exist
after the full expression which invokes the function.
Your code is wrong. You are returning a reference to a local object, and the local object is not eligible for the lifetime extension you learned about. The lifetime of local objects end before you even get a chance to assign the return value to another variable, and therefore there's no way to extend their lifetime.
The lifetime extension is for temporary objects:
Employee getEmp(int a) {
return Employee(a);
}
Employee const& tmpEmp = m.getEmp(10); //
... stuff
//end of scope - tmpEmp valid until here
Now getEmp() returns a temporary object (not a reference to a local object,) and that object is still valid when the assignment occurs. So its lifetime gets extended.