Reputation: 133
In main I have two types of objects: Person and Pet. A Person can have a number of Pets. I am creating both types of objects dynamically.
#include <iostream>
#include <string>
#include <vector>
struct Pet
{
Pet() { }
Pet(std::string name, std::string type)
{
Name = name;
Type = type;
std::cout << Name << " is created" << std::endl;
}
~Pet()
{
std::cout << Name << " is destroyed" << std::endl;
}
std::string GetName() { return Name; }
std::string GetType() { return Type; }
std::string Name;
std::string Type;
};
struct Person
{
Person(std::string name)
{
Name = name;
std::cout << Name << " is created" << std::endl;
}
~Person()
{
std::cout << Name << " is destroyed" << std::endl;
}
void AddPet(Pet& pet)
{
Pets.push_back(pet);
}
std::string GetPersonsPets()
{
if (Pets.size() == 0)
{
return Name + " has no pets";
}
if (Pets.size() == 1)
{
return Name + " has " + Pets[0].GetName() + ",a " + Pets[0].GetType();
}
}
std::string Name;
std::vector<Pet> Pets;
};
int main()
{
Person* peter = new Person("Peter");
Person* alice = new Person("Alice");
Pet* fluffy = new Pet("Flyffy", "dog");
peter->AddPet(*fluffy);
std::vector<Person*> People;
People.push_back(peter);
People.push_back(alice);
int i = 0;
for (std::vector<Person*>::iterator it = People.begin(); it != People.end();
it++)
{
std::cout << People[i]->GetPersonsPets() << std::endl;
i++;
}
//delete fluffy;
delete alice;
delete peter;
}
Everything seems to work as it should, but something interesting happens when it comes to deleting the Pet objects. When I uncomment delete fluffy
, fluffy gets deleted by itself right after peter.
I thought that, since fluffy was created dynamically, it would never be destroyed unless I did that myself?
But the other interesting thing happens when I don't uncomment delete fluffy
. Then it's destructor will be called twice.
How is it possible to destroy something twice?
Upvotes: 1
Views: 105
Reputation: 369
Remember that what goes into a container is the copy of the object you specify. In your case, copy of the object *fluffy
goes into the container. It is this copy that gets destroyed (thus calling its destructor) and not the original newed object created in this line Pet* fluffy = new Pet("Flyffy", "dog");
. Thus, your code with //delete fluffy;
results into a memory leak.
In case you consider copying the pointer itself into the container, note that when using containers of newed pointers, remember to delete the pointers before the container is destroyed (Item 7, Effective STL, Scott Meyers.) since destructing a pointer object does not call delete. Or else use smart_pointers as mentioned by @formerlyknownas_463035818
Upvotes: 0
Reputation: 122133
When you add the pet here:
peter->AddPet(*fluffy);
then *fluffy
is the pointer derferenced and here
void Person::AddPet(Pet& pet)
{
Pets.push_back(pet);
}
You store a copy of *fluffy
in some container (I suppose it is a std::vector
).
The objects you see getting destroyed are those copies (vector manages its own memory, ie it destroys its elements when the vector is destroyed). You still need to delete the dynamically allocated instances you created in main
.
Note that you better didnt use new
and delete
at all here. Use dynamic memory when you must. And when you do you better use smart pointers rather than raw pointers.
PS Your code exhibits the bad practice of writing getters just because. All the members of Pet
and Person
are public. Either make the members private or remove the getters
. Having both is misleading. Also if you do provide a default constuctor ( Pet() { }
) it should initialize the members to meaningful values. The job of a constructor is to construct an object in a valid state. Last but not least you should use the member initializer list for constructors as in
Pet(std::string name, std::string type) : Name(name),Type(type) {}
Upvotes: 5
Reputation: 22176
Pets.push_back(pet);
creates a copy of pet
and stores it inside vector. When vector is destroyed, this copy is destroyed (but the original fluffy
allocated with new
is still leaked).
When you uncomment delete fluffy;
, then you properly destroy both the original object and it's copy.
If you want to track copies of objects created, add user defined copy constructor and copy assignment operator (read up on The Rule of Three).
Upvotes: 3