在C++中返回对象

bib*_*tha 34 c++ memory-management factory

从类中返回对象时,何时是释放内存的正确时间?

例,

class AnimalLister 
{
  public:
  Animal* getNewAnimal() 
  {
    Animal* animal1 = new Animal();
    return animal1;
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我创建一个Animal Lister的实例并从中获取Animal引用,那么我应该在哪里删除它?

int main() {
  AnimalLister al;
  Animal *a1, *a2;
  a1 = al.getNewAnimal();
  a2 = al.getNewAnimal();
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是AnimalLister没有办法跟踪动物创建的列表,所以我如何更改这些代码的逻辑以便有办法删除创建的对象.

Ecl*_*pse 36

根据您的使用情况,您可以在此处使用以下几种选项:

  1. 每次创建动物时制作副本:

    class AnimalLister 
    {
    public:
      Animal getNewAnimal() 
      {
        return Animal();
      }
    };
    
    int main() {
      AnimalLister al;
      Animal a1 = al.getNewAnimal();
      Animal a2 = al.getNewAnimal();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    优点:

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

    缺点:

    • 它需要Animal一个表现良好的复制构造函数.
    • 如果大Animal而复杂,它可能涉及大量复制,尽管返回值优化可以在许多情况下缓解这种情况.
    • 如果您计划返回派生的子类,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();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    优点:

    • 使用对象层次结构(无对象切片).
    • 没有必须复制大对象的问题.
    • 无需Animal定义复制构造函数.

    缺点:

    • 需要Boost或TR1库,或其他智能指针实现.
  3. 跟踪所有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.
    
    Run Code Online (Sandbox Code Playgroud)

    优点:

    • 非常适合在Animal有限时间内需要一堆s的情况,并计划一次性释放它们.
    • 轻松适应自定义内存池并将所有内容Animal放在一个内存中delete.
    • 使用对象层次结构(无对象切片).
    • 没有必须复制大对象的问题.
    • 无需Animal定义复制构造函数.
    • 不需要外部库.

    缺点:

    • 上面描述的实现不是线程安全的
    • 需要额外的支持代码
    • 比前两个方案更不清楚
    • 不明显的是,当AnimalLister超出范围时,它会带动动物.除了挂在AnimalLister上之外,你不能再挂在动物身上了.


Chr*_*ung 24

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

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

  • 对于这种情况,std :: auto_ptr <>就足够了. (5认同)
  • C++ 0x标准将具有unique_ptr <>,它没有shared_ptr的开销,仍然可以满足您的需求. (2认同)

Igo*_*nov 8

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

std::auto_ptr< Animal> getNewAnimal() 
{
  std::auto_ptr< Animal > animal1( new Animal() );
  return animal1;
}
Run Code Online (Sandbox Code Playgroud)

如果您能够使用TR1或Boost,您还可以使用shared_ptr <>.


its*_*att 8

指针和分配内存的经典问题.这是责任 - 谁负责清理AnimalLister对象分配的内存.

您可以在AnimalLister中存储指向每个已分配动物的指针,并让它清理干净.

但是,你确实有几个指向动物坐在main()中的指针,这些指针将引用被删除的内存.

我认为引用计数解决方案比滚动自己的解决方案更有效的原因之一.


gbj*_*anb 5

  1. shared_ptr(效果很好),
  2. 返回一个简单的指针,并告诉你的班级用户现在是他们的动物,他们有责任在完成后将其删除,
  3. 实现'freeAnimal(Animal*)'方法,显然需要删除动物指针.

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

所以:

class AnimalLister 
{
Animal getAnimal() { Animal a; return a; }; // uses fast Return Value Optimisation
};

Animal myownanimal = AnimalLister.getAnimal(); // copy ctors into your Animal object
Run Code Online (Sandbox Code Playgroud)

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