Question

Est-ce une bonne façon de mettre en œuvre un comportement Enfin, comme dans la norme C ++? (Sans pointeurs spéciaux)

class Exception : public Exception
    { public: virtual bool isException() { return true; } };

class NoException : public Exception
    { public: bool isException() { return false; } };


Object *myObject = 0;

try
{
  // OBJECT CREATION AND PROCESSING
  try
  {
    myObject = new Object();

    // Do something with myObject.
  }

  // EXCEPTION HANDLING
  catch (Exception &e)
  {
    // When there is an excepion, handle or throw,
    // else NoException will be thrown.
  }

  throw NoException();
}

// CLEAN UP
catch (Exception &e)
{
  delete myObject;

  if (e.isException()) throw e;
}
  1. Pas d'exception lancée par l'objet -> NoException -> Objet nettoyé
  2. Exception lancée par l'objet -> Handled -> NoException -> Objet nettoyé
  3. Exception lancée par l'objet -> Jeté -> Exception -> Objet nettoyé -> Jeté
Était-ce utile?

La solution

La réponse standard est d'utiliser une variante de href="http://en.wikipedia.org/wiki/Resource_acquisition_is_initialization" d'allocation des ressources-est-initialisation abrégé RAII. Fondamentalement, vous construire une variable qui a la même portée que le bloc qui serait à l'intérieur du bloc avant la finalement, puis faire le travail dans le bloc finalement à l'intérieur du destructor des objets.

try {
   // Some work
}
finally {
   // Cleanup code
}

devient

class Cleanup
{
public:
    ~Cleanup()
    {
        // Cleanup code
    }
}

Cleanup cleanupObj;

// Some work.

Cela ressemble terriblement pratique, mais en général il y a un objet préexistant qui fera le nettoyage pour vous. Dans votre cas, il semble que vous voulez détruire l'objet dans le bloc finally, ce qui signifie un pointeur intelligent ou unique, fera ce que vous voulez:

std::unique_ptr<Object> obj(new Object());

ou C ++ moderne

auto obj = std::make_unique<Object>();

Peu importe où des exceptions sont levées, l'objet sera détruit. Pour en revenir à RAII, dans ce cas, l'allocation des ressources alloue la mémoire pour l'objet et la construction et l'initialisation est l'initialisation du unique_ptr.

Autres conseils

Non. La méthode standard pour construire une voie enfin comme est de séparer les préoccupations ( http://en.wikipedia.org/ wiki / Separation_of_concerns) et fabriquer des objets qui sont utilisés dans le bloc try libérer automatiquement les ressources dans leur destructor (appelé « Champ d'application lié la gestion des ressources »). Depuis Destructeurs exécuter de manière déterministe, contrairement à Java, vous pouvez compter sur eux pour nettoyer en toute sécurité. De cette façon, les objets qui ACQUIS la ressource sera également nettoyer la ressource.

Une façon qui est particulière est l'allocation dynamique de la mémoire. Puisque vous êtes une acquerront la ressource, vous devez nettoyer à nouveau. Ici, les pointeurs intelligents peuvent être utilisés.

try {
    // auto_ptr will release the memory safely upon an exception or normal 
    // flow out of the block. Notice we use the "const auto_ptr idiom".
    // http://www.gotw.ca/publications/using_auto_ptr_effectively.htm
    std::auto_ptr<A> const aptr(new A);
} 
// catch...

Si, pour une raison étrange, vous n'avez pas accès aux bibliothèques standard, il est très facile à mettre en œuvre autant que vous avez besoin d'un type de pointeur intelligent pour gérer la ressource. Il peut sembler un peu bavard, mais il est moins de code que les essayer imbriqués / blocs catch, et il vous suffit de définir ce modèle une fois de plus, au lieu d'une fois par une ressource qui a besoin de la gestion:

template<typename T>
struct MyDeletable {
    explicit MyDeletable(T *ptr) : ptr_(ptr) { }
    ~MyDeleteable() { delete ptr_; }
private:
    T *ptr_;
    MyDeletable(const MyDeletable &);
    MyDeletable &operator=(const MyDeletable &);
};

void myfunction() {
    // it's generally recommended that these two be done on one line.
    // But it's possible to overdo that, and accidentally write
    // exception-unsafe code if there are multiple parameters involved.
    // So by all means make it a one-liner, but never forget that there are
    // two distinct steps, and the second one must be nothrow.
    Object *myObject = new Object();
    MyDeletable<Object> deleter(myObject);

    // do something with my object

    return;
}

Bien sûr, si vous faites cela et utilisez ensuite RAII dans le reste de votre code, vous finirez finir par avoir besoin de toutes les fonctionnalités de la norme et Smart Boost types de pointeur. Mais ceci est un début, et fait ce que je pense que vous voulez.

L'essai ... approche de capture ne sera probablement pas fonctionner correctement face à la programmation de la maintenance. Le bloc NETTOYAGE n'est pas garanti à exécuter: par exemple, si le code retourne au début « faire quelque chose », ou jette en quelque sorte quelque chose qui n'est pas une exception. D'autre part, le destructeur de « Deleter » dans mon code est garanti à exécuter dans ces deux cas (mais pas si le programme se termine).

Mon conseil est: ne pas essayer d'imiter le comportement d'un essai-finally en C ++. Il suffit d'utiliser RAII à la place. Vous allez vivre plus heureux.

En supposant que vous cherchez à supprimer le pointeur myObject et éviter les fuites de mémoire, votre code peut encore échouer à le faire s'il y a une déclaration « retour » dans le code où vous dites // Do something with myObject. (je suppose le code réel serait ici)

techniques de RAII ont l'action pertinente qui équivaut à un bloc « enfin », dans le destructor d'un objet particulier:

class ResourceNeedingCleanup
{
  private:
    void cleanup(); // action to run at end
  public:
    ResourceNeedingCleanup( /*args here*/) {}
    ~ResourceNeedingCleanup() { cleanup(); }  

    void MethodThatMightThrowException();
};

typedef boost::shared_ptr<ResourceNeedingCleanup> ResourceNeedingCleanupPtr;
// ref-counted smart pointer


class SomeObjectThatMightKeepReferencesToResources
{
   ResourceNeedingCleanupPtr pR;

   void maybeSaveACopy(ResourceNeedingCleanupPtr& p)
   {
      if ( /* some condition is met */ )
         pR = p;
   }
};

// somewhere else in the code:
void MyFunction(SomeObjectThatMightKeepReferencesToResources& O)
{
   ResourceNeedingCleanup R1( /*parameters*/) ;
   shared_ptr<ResourceNeedingCleanup> pR2 = 
        new ResourceNeedingCleanup( /*parameters*/ );
   try
   {
      R1.MethodThatMightThrowException();
      pR2->MethodThatMightThrowException();
      O->maybeSaveACopy(pR2);
   }
   catch ( /* something */ )
   {
      /* something */
   }

   // when we exit this block, R1 goes out of scope and executes its destructor
   // which calls cleanup() whether or not an exception is thrown.
   // pR2 goes out of scope. This is a shared reference-counted pointer. 
   // If O does not save a copy of pR2, then pR2 will be deleted automatically
   // at this point. Otherwise, pR2 will be deleted automatically whenever
   // O's destructor is called or O releases its ownership of pR2 and the
   // reference count goes to zero.
}

Je crois avoir la sémantique correcte; Je ne l'ai pas utilisé shared_ptr beaucoup moi-même, mais je préfère à auto_ptr <> - un pointeur vers un objet ne peut être « possédé » par un auto_ptr <>. Je l'ai utilisé le CComPtr et une variante de ce que je l'ai moi-même écrit pour les objets « réguliers » (non-COM) qui est similaire à shared_ptr <> mais a attach () et Détacher () pour le transfert de pointeurs d'un pointeur intelligent à l'autre.

Pour répondre directement à votre question, pas .

Il est une façon intelligente de mettre en œuvre cette fonctionnalité, mais il est pas fiable. Une façon qui vous échouez est si votre code « faire quelque chose » jette une exception qui ne provient pas de Exception. Dans ce cas, vous ne serez jamais delete myObject.

Il y a une question plus importante à portée de main ici, et c'est les méthodologies adoptées par les programmeurs d'une langue particulière. La raison pour laquelle vous entendez sur le RAII est parce que les programmeurs ayant une expérience beaucoup plus que vous ou moi avons a constaté que dans le domaine de la programmation C ++, cette méthode est fiable. Vous pouvez compter sur d'autres programmeurs utilisant et autres programmeurs voulez compter sur vous de l'utiliser.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top