简体   繁体   English

用C ++返回对象

[英]Returning Objects in C++

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? 如果我创建了Animal Lister的实例并从中获取Animal引用,那么我应该在哪里删除它?

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. 这里的问题是AnimalLister无法跟踪创建的动物列表,因此我如何更改此类代码的逻辑以具有删除创建的对象的方法。

Depending on your usage, there are a couple of options you could go with here: 根据您的使用情况,您可以在此处选择两个选项:

  1. 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: 优点:

    • Easy to understand. 容易明白。
    • Requires no extra libraries or supporting code. 不需要额外的库或支持代码。

    Cons: 缺点:

    • It requires Animal to have a well-behaved copy-constructor. 它要求Animal具有行为良好的复制构造函数。
    • It can involve a lot of copying if Animal is larg and complex, although return value optimization can alleviate that in many situations. 如果Animal而复杂,则可能涉及大量复制,尽管返回值优化可以在许多情况下缓解这种情况。
    • Doesn't work if you plan on returning sub-classes derived from Animal as they will be sliced down to a plain Animal , losing all the extra data in the sub-class. 如果您计划返回从Animal派生的子类,则将不起作用,因为它们将被切成普通的Animal ,从而丢失了该子类中的所有额外数据。
  2. Return a shared_ptr<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(); } 

    Pros: 优点:

    • Works with object-hierarchies (no object slicing). 适用于对象层次结构(无对象切片)。
    • No issues with having to copy large objects. 不必复制大对象就没有问题。
    • No need for Animal to define a copy constructor. 无需Animal定义副本构造函数。

    Cons: 缺点:

    • Requires either Boost or TR1 libraries, or another smart-pointer implementation. 需要Boost或TR1库,或其他智能指针实现。
  3. Track all Animal allocations in AnimalLister 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. 

    Pros: 优点:

    • Ideal for situations where you need a bunch of Animal s for a limited amount of time, and plan to release them all at once. 非常适合需要有限时间的一堆Animal并计划一次释放它们的情况。
    • Easily adaptable to custom memory-pools and releasing all the Animal s in a single delete . 轻松适应自定义内存池,并在单个delete释放所有Animal
    • Works with object-hierarchies (no object slicing). 适用于对象层次结构(无对象切片)。
    • No issues with having to copy large objects. 不必复制大对象就没有问题。
    • No need for Animal to define a copy constructor. 无需Animal定义副本构造函数。
    • No need for external libraries. 无需外部库。

    Cons: 缺点:

    • The implementation as written above is not thread-safe 上面编写的实现不是线程安全的
    • Requires extra support code 需要额外的支持代码
    • Less clear than the previous two schemes 不如前两个方案那么清晰
    • It's non-obvious that when the AnimalLister goes out of scope, it's going to take the Animals with it. 显然,当AnimalLister超出范围时,它将带走Animals。 You can't hang on to the Animals any longer than you hang on the AnimalLister. 挂在动物上的时间不能超过挂在AnimalLister上的时间。

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. 我建议返回一个std::tr1::shared_ptr (或者,如果您的C ++实现没有TR1,则返回boost::shared_ptr )而不是原始指针。 So, instead of using Animal* , use std::tr1::shared_ptr<Animal> instead. 因此,不要使用Animal* ,而应使用std::tr1::shared_ptr<Animal>

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<>. 如果能够使用TR1或Boost,则也可以使用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. 关于责任-谁负责清理AnimalLister对象分配的内存。

You could store off a pointer to each of those allocated Animals in the AnimalLister itself and have it clean things up. 您可以在AnimalLister本身中存储一个指向每个分配的Animals的指针,并将其清理干净。

But, you do have a couple of pointers to Animals sitting there in main() that would be referencing memory that was deleted. 但是,在main()中确实有几个指向Animals的指针,它们将引用已删除的内存。

One of the reasons I think the reference counting solutions work better than rolling your own solution. 我认为引用计数解决方案比滚动自己的解决方案更好的原因之一。

  1. shared_ptr (which works well), shared_ptr(效果很好),
  2. return a simple pointer and tell the user of your class that it is their animal now, and they have the responsibility to delete it when finished, 返回一个简单的指针,并告诉您的班级用户这是他们的动物,他们有责任在完成后删除它,
  3. implement a 'freeAnimal(Animal*)' method that makes it obvious that deletion of the animal pointer is required. 实现一个“ freeAnimal(Animal *)”方法,该方法很明显要求删除动物指针。

  4. An alternative way is to simply return the animal object directly, no pointers, no calls to new. 另一种方法是简单地直接返回动物对象,没有指针,没有对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). RVO意味着实际上返回对象而不是指针是更快的(因为编译器不会创建新对象并将其复制到调用者的对象中,而是直接使用调用者的对象)。

Scott Meyers详尽讨论中 ,他得出结论,最好使用shared_ptr或auto_ptr。

Or you could follow the COM-ish approach, and apply simple reference counting. 或者,您可以遵循COM-ish方法,并应用简单的引用计数。

  • When you create the object, give it a reference value of 1 instantly 创建对象时,请立即为其指定参考值1
  • When anyone gets a copy of the pointer, they AddRef() 当任何人获得指针的副本时,他们将AddRef()
  • When anyone gives up their copy of the pointer, they Release() 当任何人放弃其指针的副本时,他们会释放()

If the reference count hits 0, the object deletes itself. 如果引用计数达到0,则对象将自身删除。

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. 它最终是shared_ptr在后台执行的操作,但是它使您可以更好地控制正在发生的事情,以我的经验,它更易于调试。 (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. 到目前为止,在我的开发中,我没有给shared_ ptr太多的机会,因此这可能完全适合您的目的。

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. 在您的特定情况下,AnimalLister类的用户请求了指向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. AnimalLister只是创建新Animal对象的工厂而已。

I really like Josh's answer, but I thought I might throw in another pattern because it hasn't been listed yet. 我真的很喜欢Josh的回答,但是我想我可能会抛出另一种模式,因为它尚未被列出。 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;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM