studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones c++ exception try-catch throw finally

programacion - Finalmente en C++



manual de programacion android pdf (6)

La respuesta estándar es utilizar alguna variante de RAII abreviada de asignación de recursos . Básicamente construyes una variable que tiene el mismo alcance que el bloque que estaría dentro del bloque antes del final, luego haces el trabajo en el bloque finally dentro del destructor de objetos.

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

se convierte

class Cleanup { public: ~Cleanup() { // Cleanup code } } Cleanup cleanupObj; // Some work.

Esto se ve terriblemente incómodo, pero generalmente hay un objeto preexistente que hará la limpieza por usted. En tu caso, parece que quieres destruir el objeto en el bloque finally, lo que significa que un puntero inteligente o automático hará lo que quieras:

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

No importa qué excepciones se arrojen, el objeto será destruido. Volviendo a RAII, en este caso la asignación de recursos es asignar la memoria para el objeto y construirlo y la inicialización es la inicialización de auto_ptr.

¿Es esta una buena manera de implementar un comportamiento similar al de C ++ estándar? (Sin punteros especiales)

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. Ninguna excepción lanzada por objeto -> NoException -> Objeto limpiado
  2. Excepción lanzada por objeto -> Manejada -> NoExcepción -> Objeto limpiado
  3. Excepción lanzada por objeto -> Lanzada -> Excepción -> Objeto limpiado -> Lanzado

Mi consejo es: no intente emular el comportamiento de una cláusula try-finally en C ++. Simplemente use RAII en su lugar. Vivirás más feliz.


No. La forma estándar de construir finalmente una forma similar es separar las preocupaciones ( http://en.wikipedia.org/wiki/Separation_of_concerns ) y hacer que los objetos que se usan dentro del bloque try liberen recursos automáticamente en su destructor (llamado " Alcance Bound Resource Management "). Como los destructores se ejecutan de manera determinista, a diferencia de Java, puede confiar en ellos para que limpien de forma segura. De esta forma, los objetos que adquirieron el recurso también limpiarán el recurso.

Una forma especial es la asignación de memoria dinámica. Como usted es quien adquiere el recurso, debe volver a limpiarlo. Aquí, se pueden usar punteros inteligentes.

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


Para responder directamente a tu pregunta, no .

Es una forma inteligente de implementar esa funcionalidad, pero no es confiable. Una forma que le fallará es si su código "hacer algo" arroja una excepción que no se deriva de Exception . En ese caso, nunca delete myObject .

Aquí hay un problema más importante, y esa es la metodología adoptada por los programadores de cualquier idioma en particular. La razón por la que escuchas acerca de RAII es porque los programadores con mucha más experiencia que tú o yo hemos encontrado que en el dominio de la programación en C ++, esa metodología es confiable. Puede confiar en que otros programadores lo usarán y otros programadores querrán confiar en que lo utilizará.


Si por alguna extraña razón no tiene acceso a las bibliotecas estándar, entonces es muy fácil implementar todo lo que necesite de un tipo de puntero inteligente para manejar el recurso. Puede parecer un poco detallado, pero es menos código que esos bloques try / catch anidados, y usted solo tiene que definir esta plantilla una vez, en lugar de una vez por cada recurso que necesita administración:

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

Por supuesto, si hace esto y luego usa RAII en el resto de su código, eventualmente terminará necesitando todas las características del estándar y aumentará los tipos de punteros inteligentes. Pero esto es un comienzo, y hace lo que creo que quieres.

El enfoque try ... catch probablemente no funcionará bien ante la programación de mantenimiento. El bloque CLEAN UP no está garantizado para ser ejecutado: por ejemplo, si el código "do something" regresa temprano, o de alguna manera arroja algo que no es una excepción. Por otro lado, se garantiza que el destructor de "eliminador" en mi código se ejecutará en ambos casos (aunque no si el programa finaliza).


Suponiendo que está buscando eliminar el puntero myObject y evitar fugas de memoria, su código aún no puede hacer esto si hay una declaración "return" en el código donde dice // Do something with myObject. (Estoy asumiendo que el código real estaría aquí)

Las técnicas de RAII tienen la acción relevante que es equivalente a un bloque "finalmente" en el destructor de un objeto en particular:

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

Creo que tengo la semántica correcta; No he usado shared_ptr mucho, pero prefiero auto_ptr <> - un puntero a un objeto solo puede ser "propiedad" de un auto_ptr <>. He usado el CComPtr de COM y una variante del mismo que he escrito para objetos "normales" (que no son COM) que es similar a shared_ptr <> pero tiene Attach () y Detach () para la transferencia de punteros desde un smart puntero a otro.