When returning objects from a class, when is the right time to release the memory?
Example,
class AnimalLister
{
public:
Animal* getNewAnimal()
{
Animal* animal1 = new Animal();
return animal1;
}
}
If i create an instance of Animal Lister and get Animal reference from it, then where am i supposed to delete it?
int main() {
AnimalLister al;
Animal *a1, *a2;
a1 = al.getNewAnimal();
a2 = al.getNewAnimal();
}
The problem here is AnimalLister doesnot have a way to track the list of Animals Created, so how do i change the logic of such code to have a way to delete the objects created.
Depending on your usage, there are a couple of options you could go with here:
Make a copy every time you create an animal:
class AnimalLister { public: Animal getNewAnimal() { return Animal(); } }; int main() { AnimalLister al; Animal a1 = al.getNewAnimal(); Animal a2 = al.getNewAnimal(); }
Pros:
Cons:
Animal
to have a well-behaved copy-constructor. Animal
is larg and complex, although return value optimization can alleviate that in many situations. Animal
as they will be sliced down to a plain Animal
, losing all the extra data in the sub-class. Return a shared_ptr<Animal>
:
class AnimalLister { public: shared_ptr<Animal> getNewAnimal() { return new Animal(); } }; int main() { AnimalLister al; shared_ptr<Animal> a1 = al.getNewAnimal(); shared_ptr<Animal> a2 = al.getNewAnimal(); }
Pros:
Animal
to define a copy constructor. Cons:
Track all Animal
allocations in AnimalLister
class AnimalLister { vector<Animal *> Animals; public: Animal *getNewAnimal() { Animals.push_back(NULL); Animals.back() = new Animal(); return Animals.back(); } ~AnimalLister() { for(vector<Animal *>::iterator iAnimal = Animals.begin(); iAnimal != Animals.end(); ++iAnimal) delete *iAnimal; } }; int main() { AnimalLister al; Animal *a1 = al.getNewAnimal(); Animal *a2 = al.getNewAnimal(); } // All the animals get deleted when al goes out of scope.
Pros:
Animal
s for a limited amount of time, and plan to release them all at once. Animal
s in a single delete
. Animal
to define a copy constructor. Cons:
I advise returning a std::tr1::shared_ptr
(or boost::shared_ptr
, if your C++ implementation does not have TR1) instead of a raw pointer. So, instead of using Animal*
, use std::tr1::shared_ptr<Animal>
instead.
Shared pointers handle reference tracking for you, and delete the object automatically if there are no references left to it.
The simpliest way is to return smart pointer instead of regular pointers. For example:
std::auto_ptr< Animal> getNewAnimal()
{
std::auto_ptr< Animal > animal1( new Animal() );
return animal1;
}
If you are able to use TR1 or Boost, you can also use shared_ptr<>.
Kind of a classic issue with pointers and allocated memory. It's about responsibility - who is responsible for cleaning up the memory allocated by the AnimalLister object.
You could store off a pointer to each of those allocated Animals in the AnimalLister itself and have it clean things up.
But, you do have a couple of pointers to Animals sitting there in main() that would be referencing memory that was deleted.
One of the reasons I think the reference counting solutions work better than rolling your own solution.
implement a 'freeAnimal(Animal*)' method that makes it obvious that deletion of the animal pointer is required.
An alternative way is to simply return the animal object directly, no pointers, no calls to new. The copy constructor will ensure the caller gets their own animal object that they can store on the heap or stack, or copy into a container as they desire.
So:
class AnimalLister
{
Animal getAnimal() { Animal a; return a; }; // uses fast Return Value Optimisation
};
Animal myownanimal = AnimalLister.getAnimal(); // copy ctors into your Animal object
RVO means that returning the object instead of the pointer is actually faster (as the compiler doesn't create a new object and copies it into the caller's object, but uses the caller's object directly).
在Scott Meyers的详尽讨论中 ,他得出结论,最好使用shared_ptr或auto_ptr。
Or you could follow the COM-ish approach, and apply simple reference counting.
If the reference count hits 0, the object deletes itself.
Its ultimately what the shared_ptr does under the hood, but it gives you more control over whats going on, and in my experience easier to debug. (Its also very cross-platform).
I haven't given shared_ ptr too much of a chance in my development as yet, so that may serve your purposes perfectly.
The time to release the memory occupied by an object is when you don't need that particular object any more. In your particular case, the user of a class AnimalLister requested a pointer to a new allocated object of class Animal. So, he's the one that is responsible for freeing memory when he does need that pointer/object any more.
AnimalLister lister;
Animal* a = lister.getNewAnimal();
a->sayMeow();
delete a;
In my opinion, there's no need to over-engineer anything in this case. AnimalLister is just a factory that creates new Animal objects and that's it.
I really like Josh's answer, but I thought I might throw in another pattern because it hasn't been listed yet. The idea is just force the client code to deal with keeping track of the animals.
class Animal
{
...
private:
//only let the lister create or delete animals.
Animal() { ... }
~Animal() { ... }
friend class AnimalLister;
...
}
class AnimalLister
{
static s_count = 0;
public:
~AnimalLister() { ASSERT(s_count == 0); } //warn if all animals didn't get cleaned up
Animal* NewAnimal()
{
++count;
return new Animal();
}
void FreeAnimal(Animal* a)
{
delete a;
--s_count;
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.