从类中返回对象时,何时是释放内存的正确时间?
实施例,
class AnimalLister
{
public:
Animal* getNewAnimal()
{
Animal* animal1 = new Animal();
return animal1;
}
}
如果我创建一个Animal Lister的实例并从中获取Animal引用,那么我应该删除它的位置?
int main() {
AnimalLister al;
Animal *a1, *a2;
a1 = al.getNewAnimal();
a2 = al.getNewAnimal();
}
问题在于AnimalLister没有办法跟踪动物创建的列表,所以如何更改此类代码的逻辑以便删除创建的对象。
答案 0 :(得分:36)
根据您的使用情况,您可以使用以下几种选项:
每次创建动物时都要制作副本:
class AnimalLister
{
public:
Animal getNewAnimal()
{
return Animal();
}
};
int main() {
AnimalLister al;
Animal a1 = al.getNewAnimal();
Animal a2 = al.getNewAnimal();
}
优点:
缺点:
Animal
拥有一个表现良好的复制构造函数。Animal
大而复杂,可能会涉及大量复制,尽管return value optimization可以在许多情况下缓解这种情况。Animal
派生的子类返回到sliced到普通Animal
,则会失效,从而丢失子中的所有额外数据类。 返回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();
}
优点:
Animal
定义复制构造函数。缺点:
跟踪Animal
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.
优点:
Animal
的情况,并计划一次性释放它们。Animal
中释放所有delete
。Animal
定义复制构造函数。缺点:
答案 1 :(得分:24)
我建议返回std::tr1::shared_ptr
(或boost::shared_ptr
,如果你的C ++实现没有TR1)而不是原始指针。因此,不要使用Animal*
,而是使用std::tr1::shared_ptr<Animal>
。
共享指针为您处理引用跟踪,如果没有任何引用,则自动删除该对象。
答案 2 :(得分:8)
最简单的方法是返回智能指针而不是常规指针。 例如:
std::auto_ptr< Animal> getNewAnimal()
{
std::auto_ptr< Animal > animal1( new Animal() );
return animal1;
}
如果您能够使用TR1或Boost,您还可以使用shared_ptr&lt;&gt;。
答案 3 :(得分:8)
指针和分配内存的经典问题。这是责任 - 谁负责清理AnimalLister对象分配的内存。
您可以在AnimalLister中存储指向每个已分配动物的指针,并让它清理干净。
但是,你确实有几个指向动物坐在main()中的指针,这些指针会引用被删除的内存。
我认为引用计数解决方案比滚动自己的解决方案更有效的原因之一。
答案 4 :(得分:5)
实现'freeAnimal(Animal *)'方法,显然需要删除动物指针。
另一种方法是直接返回动物对象,没有指针,没有新的调用。复制构造函数将确保调用者获取他们可以存储在堆或堆栈上的动物对象,或者根据需要复制到容器中。
所以:
class AnimalLister
{
Animal getAnimal() { Animal a; return a; }; // uses fast Return Value Optimisation
};
Animal myownanimal = AnimalLister.getAnimal(); // copy ctors into your Animal object
RVO意味着返回对象而不是指针实际上更快(因为编译器不创建新对象并将其复制到调用者的对象中,但直接使用调用者的对象)。
答案 5 :(得分:3)
在thorough discussion by Scott Meyers中,他得出结论,使用shared_ptr或auto_ptr是最好的。
答案 6 :(得分:2)
或者您可以遵循COM-ish方法,并应用简单的引用计数。
如果引用计数达到0,则对象将自行删除。
它最终是shared_ptr在幕后所做的事情,但它让你可以更好地控制最新情况,并且根据我的经验更容易调试。 (它也非常跨平台)。
我还没有给我的开发提供过多的机会,所以这可能完全符合你的目的。
答案 7 :(得分:2)
释放对象占用的内存的时间是您不再需要该特定对象的时间。在您的特定情况下,AnimalLister类的用户请求指向Animal类的新分配对象的指针。所以,当他确实需要指针/对象时,他就是那个负责释放内存的人。
AnimalLister lister;
Animal* a = lister.getNewAnimal();
a->sayMeow();
delete a;
在我看来,在这种情况下没有必要过度设计任何东西。 AnimalLister只是一个创建新Animal对象的工厂,就是这样。
答案 8 :(得分:0)
我真的很喜欢Josh的回答,但我想我可能会投入另一种模式,因为它尚未列出。这个想法只是迫使客户代码处理跟踪动物的问题。
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;
}
}