27 votes

Renvoyer des objets en C ++

Lors du retour d'objets d'une classe, quel est le bon moment pour libérer la mémoire?

Exemple,

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

Si je crée une instance de Animal Lister et que j'obtiens une référence animale, alors où dois-je le supprimer?

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

Le problème ici est qu'AnimalLister n'a pas de moyen de suivre la liste des animaux créés, alors comment changer la logique d'un tel code pour avoir un moyen de supprimer les objets créés.

32voto

Eclipse Points 27662

Selon votre utilisation, il ya un couple d'options que vous pourriez aller avec ici:

  1. Faire une copie à chaque fois que vous créez un animal:

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

    Pour:

    • Facile à comprendre.
    • Nécessite pas de bibliothèques supplémentaires ou code de prise en charge.

    Inconvénients:

    • Il exige Animal d'avoir un bon comportement de constructeur par copie.
    • Il peut impliquer beaucoup de copie si Animal est grand et complexe, bien que la valeur de retour d'optimisation peut soulager que dans de nombreuses situations.
    • Ne fonctionne pas, si vous prévoyez retourner sous-classes dérivées Animal car ils seront tranchés jusqu'à une plaine Animal, perdant toutes les données supplémentaires dans la sous-classe.
  2. De retour d'un 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();
    }
    

    Pour:

    • Fonctionne avec objet-hiérarchies (pas d'objet de découpage).
    • Pas de problèmes avec le fait d'avoir à copier des objets de grande taille.
    • Pas besoin d' Animal de définir un constructeur de copie.

    Inconvénients:

    • Il faut soit augmenter ou de TR1 bibliothèques, ou un autre smart pointeur de la mise en œuvre.
  3. Suivre tous Animal des allocations en 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.
    

    Pour:

    • Idéal pour les situations où vous avez besoin d'un tas de Animals pour une quantité limitée de temps, et un plan pour libérer une fois pour toutes.
    • Facilement adaptable à la coutume de la mémoire-bassins et en libérant tous les Animals dans un seul delete.
    • Fonctionne avec objet-hiérarchies (pas d'objet de découpage).
    • Pas de problèmes avec le fait d'avoir à copier des objets de grande taille.
    • Pas besoin d' Animal de définir un constructeur de copie.
    • Pas besoin de bibliothèques externes.

    Inconvénients:

    • La mise en œuvre comme écrit ci-dessus n'est pas thread-safe
    • Nécessite un soutien supplémentaire code
    • Moins clair que les deux précédents régimes
    • C'est évident que lorsque l'AnimalLister est hors de portée, il va prendre les Animaux. Vous ne pouvez pas accrocher sur les Animaux plus que vous accrocher sur le AnimalLister.

24voto

Chris Jester-Young Points 102876

Je conseille de renvoyer un std::tr1::shared_ptr (ou boost::shared_ptr , si votre implémentation C ++ n'a pas TR1) au lieu d'un pointeur brut. Donc, au lieu d'utiliser Animal* , utilisez plutôt std::tr1::shared_ptr<Animal> .

Les pointeurs partagés gèrent le suivi des références pour vous et suppriment automatiquement l'objet s'il ne reste aucune référence.

8voto

Igor Semenov Points 1232

Le moyen le plus simple consiste à renvoyer un pointeur intelligent au lieu de pointeurs réguliers. Par exemple:

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

Si vous pouvez utiliser TR1 ou Boost, vous pouvez également utiliser shared_ptr <>.

8voto

itsmatt Points 18905

Une sorte de classique d'un problème avec les pointeurs et de la mémoire allouée. C'est une question de responsabilité - qui est responsable du nettoyage de la mémoire allouée par le AnimalLister objet.

Vous pouvez stocker hors d'un pointeur à chacun de ceux alloués Animaux dans la AnimalLister lui-même et de l'avoir propre des choses.

Mais, vous avez un couple de pointeurs sur des Animaux assis là dans main() qui seraient faisant référence à la mémoire qui a été supprimé.

L'une des raisons pour lesquelles je pense que le comptage de référence des solutions qui fonctionnent mieux que rouler votre propre solution.

5voto

gbjbaanb Points 31045
  1. shared_ptr (qui fonctionne bien),
  2. de retour d'un simple pointeur et indiquer à l'utilisateur de votre classe que c'est leur animal maintenant, et ils ont la responsabilité de le supprimer lorsque vous avez terminé,
  3. mettre en œuvre un "freeAnimal(Animal*)" méthode qui rend évident que la suppression de l'animal pointeur est nécessaire.

  4. Une autre façon est de simplement retourner l'animal objet directement, pas de pointeurs, pas d'appels à nouveau. Le constructeur de copie sera d'assurer l'appelant obtient leur propre animal de l'objet qu'ils peuvent stocker sur le tas ou de la pile, ou copiez-le dans un conteneur comme ils le désirent.

Donc:

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

Animal myownanimal = AnimalLister.getAnimal(); // copy ctors into your Animal object

RVO signifie que le retour de l'objet au lieu de le pointeur est en fait plus rapide (comme le compilateur ne pas créer un nouvel objet et la copie dans l'appelant de l'objet, mais les utilisations de l'appelant (objet direct).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X