Domanda

È un buon modo per implementare un comportamento simile a quello finale nel C++ standard?(Senza puntatori speciali)

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. Nessuna eccezione generata dall'oggetto -> NoException -> Oggetto ripulito
  2. Eccezione lanciata dall'oggetto -> Gestito -> NoException -> Oggetto ripulito
  3. Eccezione lanciata dall'oggetto -> Lanciato -> Eccezione -> Oggetto ripulito -> Lanciato
È stato utile?

Soluzione

La risposta standard è utilizzare qualche variante di l'allocazione-delle-risorse-è-inizializzazione abbreviato RAII.Fondamentalmente costruisci una variabile che ha lo stesso ambito del blocco che si troverebbe all'interno del blocco prima di finalmente, quindi esegui il lavoro nel blocco finalmente all'interno del distruttore di oggetti.

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

diventa

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

Cleanup cleanupObj;

// Some work.

Sembra terribilmente scomodo, ma di solito c'è un oggetto preesistente che farà la pulizia per te.Nel tuo caso, sembra che tu voglia distruggere l'oggetto nel blocco finale, il che significa che un puntatore intelligente o unico farà quello che vuoi:

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

o il moderno C++

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

Non importa quali eccezioni vengano lanciate, l'oggetto verrà distrutto.Tornando a RAII, in questo caso l'allocazione delle risorse sta allocando la memoria per l'oggetto e costruendolo e l'inizializzazione è l'inizializzazione di unique_ptr.

Altri suggerimenti

No. Il modo standard per costruire un modo finalmente come è quello di separare le preoccupazioni ( http://en.wikipedia.org/ wiki / Separation_of_concerns ) e realizzare oggetti che vengono utilizzati all'interno del blocco try rilasciare automaticamente le risorse a loro distruttore (chiamati "Ambito Bound Gestione delle risorse"). Dal momento che i distruttori eseguire in modo deterministico, a differenza di Java, si può fare affidamento su di loro per ripulire in modo sicuro. In questo modo gli oggetti che acquisito la risorsa sarà anche pulire la risorsa.

Un modo che è speciale è allocazione dinamica della memoria. Dal momento che tu sei quella aquiring la risorsa, è necessario pulire di nuovo. Qui, puntatori intelligenti possono essere utilizzati.

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...

Se per qualche strana ragione non si ha accesso alle librerie standard, quindi è molto facile da implementare quanto è necessario un tipo di puntatore intelligente per gestire la risorsa. Può sembrare un po 'prolisso, ma è meno codice di quei blocchi try / catch nidificato, e hai solo di definire questo modello una volta mai, invece di una volta per ogni risorsa che ha bisogno di gestione:

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

Naturalmente, se si fa questo e quindi utilizzare RAII nel resto del codice, avrete finalmente finire che necessitano di tutte le caratteristiche dello standard e aumentare i tipi di puntatore intelligente. Ma questo è un inizio, e fa quello che penso che si desidera.

L'approccio try ... catch probabilmente non funziona bene a fronte di programmazione di manutenzione. Il blocco PULIZIA non è garantito da eseguire: ad esempio, se il codice "fare qualcosa" torna presto, o in qualche modo getta qualcosa che non è un'eccezione. D'altra parte, il distruttore di "deleter" nel mio codice è garantito per essere eseguito in entrambi i casi (anche se non se il programma termina).

Il mio consiglio è: non cercare di emulare il comportamento di una clausola try-finally in C ++. Basta usare invece Raii. Vivrai più felice.

Supponendo che si sta cercando di eliminare il myObject puntatore ed evitare perdite di memoria, il codice può ancora non riescono a fare questo se v'è una dichiarazione "ritorno" nel codice in cui si dice // Do something with myObject. (Io parto dal presupposto codice vero e proprio sarebbe qui)

tecniche RAII hanno l'azione pertinente che è equivalente ad un blocco "finalmente", in distruttore di un particolare oggetto:

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

Credo di avere la semantica corretta; Non ho usato shared_ptr molto me stesso, ma io preferisco a auto_ptr <> - un puntatore ad un oggetto può essere "posseduto" da un solo auto_ptr <>. Ho usato CComPtr e una variante di essa che io stesso ho scritto per (non-COM) oggetti "regolari" che è simile a shared_ptr <>, ma ha Attach () e Detach () per il trasferimento di puntatori da un puntatore intelligente ad un altro.

Per rispondere direttamente alla tua domanda, non .

E 'un modo intelligente per implementare tale funzionalità, ma non è affidabile. Un modo che non riuscirà voi è se il codice "fare qualcosa" genera un'eccezione che non deriva da Exception. In questo caso, non si sarà mai delete myObject.

C'è una questione più importante a portata di mano qui, e questo è le metodologie adottate dai programmatori di qualsiasi lingua. Il motivo per cui stai sentir parlare di RAII è perché i programmatori con molta più esperienza di voi o io hanno ha rilevato che nel campo della programmazione C ++, che la metodologia è affidabile. Potete contare su altri programmatori di usarlo e altri programmatori si vuole fare affidamento su di voi usarlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top