在C ++中返回对象

当从一个类中返回对象时,什么时候释放内存?

例,

class AnimalLister { public: Animal* getNewAnimal() { Animal* animal1 = new Animal(); return animal1; } } 

如果我创build动物列表的一个实例,并从它得到动物的参考,那么我应该删除它呢?

 int main() { AnimalLister al; Animal *a1, *a2; a1 = al.getNewAnimal(); a2 = al.getNewAnimal(); } 

这里的问题是AnimalLister没有办法跟踪创build的动物列表,所以我如何改变这种代码的逻辑来删除创build的对象。

根据您的使用情况,您可以在这里select几个选项:

  1. 每次创build动物时都要复制一份:

     class AnimalLister { public: Animal getNewAnimal() { return Animal(); } }; int main() { AnimalLister al; Animal a1 = al.getNewAnimal(); Animal a2 = al.getNewAnimal(); } 

    优点:

    • 容易明白。
    • 不需要额外的库或支持代码。

    缺点:

    • 它要求Animal有一个行为良好的复制构造函数。
    • 如果Animal比较复杂,可能会涉及很多复制,尽pipe返回值优化可以减轻很多情况。
    • 如果你计划从Animal派生出来的子类,那么它们就不能工作,因为它们会被分割成一个普通的Animal ,从而失去了子类中的所有额外数据。
  2. 返回一个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定义一个拷贝构造函数。

    缺点:

    • 需要Boost或TR1库或其他智能指针实现。
  3. 跟踪AnimalLister所有Animal分配

     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定义一个拷贝构造函数。
    • 不需要外部库。

    缺点:

    • 上面写的实现不是线程安全的
    • 需要额外的支持代码
    • 比前两个scheme不太清楚
    • 当AnimalLister超出范围的时候,它将会把动物带走。 你不能再挂在动物列表上了。

我build议返回一个std::tr1::shared_ptr (或boost::shared_ptr ,如果您的C ++实现没有TR1)而不是原始指针。 所以,而不是使用Animal* ,而是使用std::tr1::shared_ptr<Animal>

共享指针为您处理引用跟踪,如果没有引用,则自动删除对象。

最简单的方法是返回智能指针,而不是常规指针。 例如:

 std::auto_ptr< Animal> getNewAnimal() { std::auto_ptr< Animal > animal1( new Animal() ); return animal1; } 

如果您可以使用TR1或Boost,也可以使用shared_ptr <>。

指针和分配内存的经典问题。 这是关于责任 – 谁负责清理由AnimalLister对象分配的内存。

你可以在AnimalLister本身存储一个指向动物分配的每个动物的指针,并将其清理干净。

但是,你在main()中有一些指向坐在那里的动物的指针,它会引用被删除的内存。

我认为参考计数解决scheme比滚动您自己的解决scheme更好的原因之一。

  1. shared_ptr(运行良好),
  2. 返回一个简单的指针,告诉你的类的用户现在是他们的动物,他们有责任在完成时删除它,
  3. 实现一个“自由动物(动物*)”的方法,很明显,删除动物指针是必需的。

  4. 另一种方法是直接返回动物对象,没有指针,没有呼叫新的。 复制构造函数将确保调用者获得他们自己的动物对象,他们可以将其存储在堆或栈中,或者根据需要复制到容器中。

所以:

 class AnimalLister { Animal getAnimal() { Animal a; return a; }; // uses fast Return Value Optimisation }; Animal myownanimal = AnimalLister.getAnimal(); // copy ctors into your Animal object 

RVO意味着返回对象而不是指针实际上更快(因为编译器不会创build新对象并将其复制到调用者的对象中,而是直接使用调用者的对象)。

在Scott Meyers的深入讨论中 ,他总结说使用shared_ptr或auto_ptr是最好的。

或者你可以按照COM-ISH的方法,并应用简单的引用计数。

  • 创build对象时,立即给它一个参考值1
  • 当任何人得到指针的副本时,他们AddRef()
  • 当任何人放弃指针的副本时,他们释放()

如果引用计数达到0,则该对象将自行删除。

它最终是shared_ptr在底层做了什么,但是它让你更好地控制正在发生的事情,并且以我的经验更容易debugging。 (它也是非常跨平台的)。

我还没有给我一个机会,所以可以完美地为你的目的服务。

释放对象占用的内存的时间是当你不再需要这个特定对象的时候。 在您的具体情况下,AnimalLister类的用户请求一个指向Animal类的新分配对象的指针。 所以,当他需要那个指针/对象的时候,他是负责释放内存的人。

 AnimalLister lister; Animal* a = lister.getNewAnimal(); a->sayMeow(); delete a; 

在我看来,在这种情况下,没有必要过度devise任何东西。 AnimalLister只是一个创build新的动物对象的工厂,就是这样。

我真的很喜欢乔希的回答,但是我想我可能会因为还没有列出来而换个模式。 这个想法是强制客户端代码来处理跟踪动物。

 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; } }