Pregunta

Estado haciendo Java para el número de años por lo que no ha sido el seguimiento de C++.Ha finalmente cláusula sido añadido a excepciones de C++ en la definición del lenguaje?

Hay una favorecido lenguaje que imita Java try/finally?

También soy molestado que C++ no tiene un final super tipo para todas las posibles excepciones que podrían ser lanzados como Java Arrojadiza de la clase.

Me pueden escribir a:

try {
  // do something
} catch(...) {
  // alas, can't examine the exception
  // can only do cleanup code and perhaps rethrow, ala:
  throw;
}

ADICIÓN DE EDICIÓN:

Acabé aceptando la respuesta que tuvo la mayoría de votos, es decir, el uso de destructores para hacer la limpieza.Por supuesto, a partir de mis propios comentarios, está claro que yo no del todo de acuerdo con eso.Sin embargo, en C++ es lo que es y por lo que en la aplicación esfuerzo que tengo en mente, voy más o menos se esfuerzan a que se adhieran a los comunes de la comunidad en la práctica.Voy a utilizar las clases de plantilla para envoltura de recursos que no tienen un destructor de la clase (es decir, de la biblioteca C recursos), confiriendo así en ellos destructor de la semántica.

NUEVO ANEXO DE EDICIÓN:

Hmm, en lugar de finalmente luego de un cierre característica tal vez?Un cierre combinado con ScopeGuard (ver uno de los las respuestas a continuación), sería una forma de realizar la limpieza arbitraria las acciones y el acceso a la limpieza código del alcance externo contexto.La limpieza puede realizarse en el lenguaje de la moda que se ve en la programación Ruby donde el suministro de limpieza de cuadras cuando un recurso se abrió.No es un cierre de la característica que está siendo considerado para C++?

¿Fue útil?

Solución

Haciendo uso efectivo de destructores. Cuando se lanza una excepción en un bloque de prueba, cualquier objeto creado dentro de él se destruirá inmediatamente (y, por lo tanto, se llama su destructor).

Esto es diferente de Java, donde no tienes idea de cuándo se llamará al finalizador de un objeto.

ACTUALIZACIÓN : Directamente desde la boca del caballo: ¿Por qué C ++ no proporciona un & Quot; finalmente & Quot; construir?

Otros consejos

Mi $.02.He sido programación en manejo de lenguajes como C# y Java para años, pero se vio obligado a hacer el cambio a la de C++ para los fines de la velocidad.Al principio no lo podía creer cómo había que escribir la firma del método dos veces en el archivo de encabezado y, a continuación, el archivo cpp, y no me gusta cómo no había por último bloque, y no hay recolección de basura significaba el seguimiento de las pérdidas de memoria en todas partes - caramba no me gusta en absoluto!

Sin embargo, como ya he dicho me vi obligado a usar C++.Así que me vi obligado a aprender en serio, y ahora por fin lo he entendido toda la programación de las expresiones idiomáticas como RAII y tengo todas las sutilezas de la lengua y tal.Me tomó un tiempo, pero ahora veo cuán diferente de un idioma es en comparación con C# o Java.

En estos días creo que C++ es el mejor lenguaje que hay!Sí, puedo entender que hay un poco más de lo que yo llamo la 'paja' a veces (aparentemente innecesario de cosas para escribir), pero después de hecho el uso de la lengua, en serio, he cambiado mi opinión acerca de él por completo.

Yo solía tener pérdidas de memoria todo el tiempo.Yo solía escribir todo el código en el .h archivo porque odiaba la separación de código, yo no podía entender por qué iban a hacer eso!Y yo que siempre terminan con estúpido cíclico incluyen dependencias, y montones más.Yo estaba realmente colgó en C# o Java, para mí C++ fue un gran paso hacia abajo.En estos días voy a conseguir.Yo casi nunca tienen pérdidas de memoria, me gusta la separación de la interfaz y de implementación, y no tengo problemas con el ciclo de las dependencias más.

Y no echo de menos el bloque finally bien.Para ser honesto, mi opinión es que estos programadores de C++ que hable acerca de la escritura de repetidas acciones de limpieza de los bloques catch solo sonido a mí me parece que es sólo una mala programadores de C++.Quiero decir, que no se parece a ninguno de los otros programadores de C++ en este hilo tiene cualquiera de los problemas que usted menciona.RAII que realmente hace que finalmente redundante, y si algo es, es menos trabajo.Escribir un destructor y, a continuación, usted nunca tendrá que escribir otro finalmente nunca!Bueno, al menos para ese tipo.

Con respeto, lo que yo creo que está pasando es que estás acostumbrado a Java ahora, justo como yo lo había sido.

La respuesta de C ++ es RAII: el destructor del objeto se ejecutará cuando salga del alcance. Ya sea por una devolución, por una excepción o lo que sea. Si maneja la excepción en otro lugar, puede estar seguro de que todos los objetos desde la función llamada hasta su controlador se destruirán correctamente haciendo que se llame a su destructor. Ellos limpiarán por ti.

Leer http://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

No finalmente no se ha agregado a C ++, ni es probable que alguna vez se agregue.

La forma en que C ++ usa constructor / destructor hace que la necesidad de finalmente sea innecesaria.
Si está utilizando catch (...) para limpiar, entonces no está utilizando C ++ correctamente. El código de limpieza debería estar en el destructor.

Aunque no es un requisito usarlo, C ++ tiene una excepción std ::.
Obligar a los desarrolladores a derivar de una clase específica para usar excepciones va en contra de la filosofía simple de C ++. También es por eso que no requerimos que todas las clases deriven de Object.

Leer: ¿C ++ admite bloques 'finalmente'? (¿Y de qué habla este 'RAII'?)

El uso de finalmente es más propenso a errores que los destructores para limpiar.
Esto se debe a que está obligando al usuario del objeto a realizar la limpieza en lugar del diseñador / implementador de la clase.

Ok, tengo que agregar una respuesta a los puntos que hizo en una publicación de respuesta separada: (Sería mucho más conveniente si hubiera editado esto en la pregunta original, por lo que no termina en la parte inferior debajo las respuestas a ella.

  

Si toda la limpieza siempre se realiza en   destructores entonces no necesitaría   ser cualquier código de limpieza en una captura   bloque - sin embargo, C ++ tiene bloques de captura donde   se realizan acciones de limpieza. De hecho   tiene un bloque para la captura (...) donde está   solo es posible realizar acciones de limpieza   (bueno, ciertamente no puedo llegar a ninguna   información de excepción para hacer cualquier   registro).

catch tiene un propósito completamente separado, y como programador de Java debes ser consciente de eso. La cláusula final es para & Quot; incondicional & Quot; acciones de limpieza No importa cómo salga el bloque, esto debe hacerse. La captura es para limpieza condicional. Si se produce este tipo de excepción, debemos realizar algunas acciones adicionales.

  

La limpieza en un bloque finalmente   terminar si había un   excepción lanzada o no, que es   lo que uno siempre quiere que suceda cuando   el código de limpieza existe.

¿En serio? Si queremos que siempre suceda para este tipo (por ejemplo, siempre queremos cerrar una conexión de base de datos cuando hayamos terminado con ella), entonces ¿por qué no la definimos una vez ? En el tipo en sí? ¿Hacer que la conexión de la base de datos se cierre a sí misma, en lugar de tener que probar / finalmente cada uso de la misma?

Ese es el punto en destructores. Garantizan que cada tipo puede encargarse de su propia limpieza, cada vez que se usa, sin que la persona que llama tenga que pensar en ello.

  

Los desarrolladores de C ++ desde el primer día han sido   plagado de tener que repetir la limpieza   acciones que aparecen en bloques de captura en   el flujo de código que ocurre sobre   salida exitosa del bloque try.   Los programadores de Java y C # simplemente lo hacen   una vez en el bloque finalmente.

No. Los programadores de C ++ nunca se han visto afectados por eso. Los programadores de C tienen. Y programadores de C que se dieron cuenta de que C ++ tenía clases, y luego se llamaron a sí mismos programadores de C ++.

Programo en C ++ y C # a diario, y siento que estoy plagado de la ridícula insistencia de C # de que debo proporcionar una cláusula final (o un bloque using) CADA VEZ QUE utilizo una conexión de base de datos o alguna otra cosa que deba ser limpiado.

C ++ me permite especificar de una vez por todas que " cada vez que hayamos terminado con este tipo, debería realizar estas acciones " ;. No me arriesgo a olvidar liberar memoria. No me arriesgo a olvidar cerrar los identificadores de archivo, los sockets o las conexiones de la base de datos. Porque mi memoria, mis manijas, tomas y conexiones db lo hacen ellos mismos.

¿Cómo puede ser nunca preferible tener que escribir un código de limpieza duplicado cada vez que usa un tipo? Si necesita ajustar el tipo porque no tiene un destructor en sí, tiene dos opciones fáciles:

  • Busque una biblioteca C ++ adecuada que proporcione este destructor (sugerencia: Boost)
  • Use boost :: shared_ptr para envolverlo y proporcionarle un functor personalizado en tiempo de ejecución, especificando la limpieza a realizar.
  

Cuando escribe servidor de aplicaciones   software como servidores de aplicaciones Java EE   Glassfish, JBoss, etc., quieres ser   capaz de atrapar y registrar excepciones   información - en lugar de dejarlo   caer al suelo O peor caer en   el tiempo de ejecución y causar un desagradecido   Salida abrupta del servidor de aplicaciones.   Por eso es muy deseable tener   una clase base global para cualquier   posible excepción   Y C ++ tiene tal clase. std :: excepción.

     

He hecho C ++ desde los días de CFront   y Java / C # la mayor parte de esta década. Es   claro para ver que hay un enorme   brecha cultural en cómo fundamentalmente   se abordan cosas similares.

No, nunca has hecho C ++. Has hecho CFront o C conclases No es C ++. Hay una gran diferencia Deja de decir que las respuestas son malas, y podrías aprender algo sobre el idioma que creías saber. ;)

Las funciones de limpieza, en sí mismas, son completamente aburridas. Tienen baja cohesión, ya que se espera que realicen una serie de actividades solo relacionadas cuando suceden. Tienen un alto acoplamiento, ya que necesitan modificar sus componentes internos cuando las funciones que realmente hacen algo cambian. Debido a esto, son propensos a errores.

El intento ... finalmente construir es un marco para las funciones de limpieza. Es una forma alentada por el lenguaje para escribir código pésimo. Además, dado que alienta a escribir el mismo código de limpieza una y otra vez, socava el principio DRY.

La forma C ++ es preferible para estos propósitos. El código de limpieza de un recurso se escribe precisamente una vez, en el destructor. Está en el mismo lugar que el resto del código para ese recurso y, por lo tanto, tiene buena cohesión. El código de limpieza no tiene que colocarse en módulos no relacionados y, por lo tanto, esto reduce el acoplamiento. Está escrito precisamente una vez, cuando está bien diseñado.

Además, la forma C ++ es mucho más uniforme. C ++, con las adiciones de puntero inteligente, maneja todo tipo de recursos de la misma manera, mientras que Java maneja bien la memoria y proporciona construcciones inadecuadas para liberar otros recursos.

Hay muchos problemas con C ++, pero este no es uno de ellos. Hay formas en que Java es mejor que C ++, pero esta no es una de ellas.

Java estaría mucho mejor con una forma de implementar RAII en lugar de intentar ... finalmente.

Para evitar tener que definir una clase de contenedor para cada recurso liberable, puede estar interesado en ScopeGuard ( http : //www.ddj.com/cpp/184403758 ) que permite crear " limpiadores " sobre la marcha.

Por ejemplo:

FILE* fp = SomeExternalFunction();
// Will automatically call fclose(fp) when going out of scope
ScopeGuard file_guard = MakeGuard(fclose, fp);

Un ejemplo de lo difícil que es usarlo finalmente correctamente.

Abrir y cerrar dos archivos.
Donde desea garantizar que el archivo se cierra correctamente.
Esperar el GC no es una opción, ya que los archivos pueden reutilizarse.

En C ++

void foo()
{
    std::ifstream    data("plop");
    std::ofstream    output("plep");

    // DO STUFF
    // Files closed auto-magically
}

En un lenguaje sin destructores pero tiene una cláusula finalmente.

void foo()
{
    File            data("plop");
    File            output("plep");

    try
    {
        // DO STUFF
    }
    finally
    {
        // Must guarantee that both files are closed.
        try {data.close();}  catch(Throwable e){/*Ignore*/}
        try {output.close();}catch(Throwable e){/*Ignore*/}
    }
}

Este es un ejemplo simple y el código ya se está volviendo complicado. Aquí solo estamos tratando de reunir 2 recursos simples. Pero a medida que aumenta el número de recursos que deben gestionarse y / o aumenta su complejidad, el uso de un bloque finalmente se vuelve cada vez más difícil de usar correctamente en presencia de excepciones.

El uso de finalmente traslada la responsabilidad del uso correcto al usuario de un objeto. Al usar el mecanismo constructor / destructor proporcionado por C ++, usted traslada la responsabilidad del uso correcto al diseñador / implementador de la clase. Esto es heredablemente más seguro ya que el diseñador solo necesita hacerlo correctamente una vez a nivel de clase (en lugar de que diferentes usuarios intenten hacerlo de diferentes maneras).

Utilizando C ++ 11 con sus expresiones lambda , he Recientemente comencé a usar el siguiente código para imitar finally:

class FinallyGuard {
private:
  std::function<void()> f_;
public:
  FinallyGuard(std::function<void()> f) : f_(f) { }
  ~FinallyGuard() { f_(); }
};

void foo() {
  // Code before the try/finally goes here
  { // Open a new scope for the try/finally
    FinallyGuard signalEndGuard([&]{
      // Code for the finally block goes here
    });
    // Code for the try block goes here
  } // End scope, will call destructor of FinallyGuard
  // Code after the try/finally goes here
}

El FinallyGuard es un objeto que se construye con un argumento similar a una función invocable, preferiblemente una expresión lambda. Simplemente recordará esa función hasta que se llame a su destructor, que es el caso cuando el objeto se sale del alcance, ya sea debido al flujo de control normal o debido al desenrollado de la pila durante el manejo de excepciones. En ambos casos, el destructor llamará a la función, ejecutando así el código en cuestión.

Es un poco extraño que tenga que escribir el código para el try antes del código para el bloque std::function<void()>, pero aparte de eso, en realidad se parece mucho a un < => / <=> de Java. Supongo que no se debe abusar de esto para situaciones en las que un objeto con su propio destructor adecuado sería más apropiado, pero hay casos en los que considero que este enfoque anterior es más adecuado. Discutí uno de esos escenarios en esta pregunta .

Hasta donde yo entiendo, <=> usará alguna indirección de puntero y al menos una llamada a función virtual para realizar su tipo de borrado , por lo que habrá una sobrecarga de rendimiento . No use esta técnica en un circuito cerrado donde el rendimiento es crítico. En esos casos, un objeto especializado cuyo destructor hace una sola cosa sería más apropiado.

Los

destructores de C ++ hacen que finally sea redundante. Puede obtener el mismo efecto moviendo el código de limpieza de finalmente a los destructores correspondientes.

Creo que le falta el punto de lo que catch (...) pueden hacer.

Dices en tu ejemplo "por desgracia, no se puede examinar la excepción".Bien, usted no tiene ninguna información sobre el tipo de la excepción.Usted incluso no sé si es un polimórficos tipo así que incluso si usted tenía algún tipo de un tipo de referencia a ella, que no podía ni siquiera intentar una forma segura dynamic_cast.

Si usted sabe acerca de ciertas excepciones o de excepción de las jerarquías que se puede hacer algo con el entonces este es el lugar para bloques catch con explícitamente al llamado de los tipos.

catch (...) no es a menudo útil en C++.Puede ser utilizado en lugares que tienen que garantizar que no tires, o sólo echar a determinados contratado excepciones.Si usted está usando catch (...) para la limpieza, a continuación, hay una muy buena posibilidad de que su código no es robusta excepción de seguro, en cualquier caso.

Como se ha mencionado en otras respuestas, si usted está utilizando objetos locales para administrar los recursos (RAII), entonces esto puede ser sorprendente y esclarecedor de cómo unos pocos bloques catch que usted necesita, a menudo, si usted no necesita hacer nada a nivel local con una excepción - incluso el bloque try puede ser redundante, mientras que las excepciones de flujo para el código de cliente que puede responder a ellos, mientras que todavía garantizar que no habrá problemas de recursos.

Para responder a tu pregunta original, si usted necesita alguna pieza de código a ejecutar al final de un bloque, excepción o no es la excepción, a continuación, una receta podría ser.

class LocalFinallyReplacement {
    ~LocalFinallyReplacement() { /* Finally code goes here */ }
};
// ...
{ // some function...
    LocalFinallyReplacement lfr; // must be a named object

    // do something
}

Nota cómo podemos hacer lejos con try, catch y throw.

Si había datos en la función para la que fue declarada originalmente fuera del bloque try que usted necesita el acceso a en el bloque "finally", entonces puede que tenga que agregar que para el constructor de la clase auxiliar y almacenarla hasta el destructor.Sin embargo, en este punto me gustaría reconsiderar seriamente si el problema puede ser resuelto por alterar el diseño de los recursos locales de manipulación de objetos puesto que implicaría que algo falla en el diseño.

No está completamente fuera del tema.

Limpieza de recursos de DB de revestimiento de calderas en Java

modo sarcasmo: ¿no es maravilloso el idioma de Java?

He hecho un montón de diseño de clase y diseño de envoltorio de plantilla en C ++ durante esos 15 años y lo he hecho todo en C ++ en términos de limpieza de destructores. Sin embargo, cada proyecto también incluía invariablemente el uso de bibliotecas C que proporcionaban recursos con el modelo de uso abrirlo, usarlo y cerrarlo. Un intento / finalmente significaría que dicho recurso solo puede consumirse donde necesita estar, de una manera completamente sólida, y terminar con él. El enfoque menos tedioso para programar esa situación. Podría lidiar con todos los demás estados que ocurren durante la lógica de esa limpieza sin tener que ser explorado en algún destructor de envoltorios.

Hice la mayor parte de mi codificación C ++ en Windows, por lo que siempre podría recurrir al uso de __try / __ de Microsoft finalmente para tales situaciones. (Su manejo estructurado de excepciones tiene algunas habilidades poderosas para interactuar con excepciones). Por desgracia, no parece que el lenguaje C haya ratificado alguna construcción portátil de manejo de excepciones.

Sin embargo, esa no era la solución ideal, porque no era sencillo mezclar el código C y C ++ en un bloque de prueba donde cualquiera de los estilos de excepción podría arrojarse. Un bloque finalmente agregado a C ++ habría sido útil para esas situaciones y permitiría la portabilidad.

Con respecto a su edición de adición, sí se están considerando cierres para C ++ 0x. Se pueden usar con protectores de alcance RAII para proporcionar una solución fácil de usar, marque weblog de Pizer . También se pueden usar para imitar try-finally, consulte esta respuesta ; pero es esto realmente ¿Una buena idea? .

Pensé en agregar mi propia solución a esto: una especie de envoltorio de puntero inteligente para cuando tienes que lidiar con tipos que no son RAII.

Usado así:

Finaliser< IMAPITable, Releaser > contentsTable;
// now contentsTable can be used as if it were of type IMAPITable*,
// but will be automatically released when it goes out of scope.

Así que aquí está la implementación de Finaliser:

/*  Finaliser
    Wrap an object and run some action on it when it runs out of scope.
    (A kind of 'finally.')
    * T: type of wrapped object.
    * R: type of a 'releaser' (class providing static void release( T* object )). */
template< class T, class R >
class Finaliser
{
private:
    T* object_;

public:
    explicit Finaliser( T* object = NULL )
    {
        object_ = object;
    }

    ~Finaliser() throw()
    {
        release();
    }

    Finaliser< T, R >& operator=( T* object )
    {
        if (object_ != object && object_ != NULL)
        {
            release();
        }
        object_ = object;

        return *this;
    }

    T* operator->() const
    {
        return object_;
    }

    T** operator&()
    {
        return &object_;
    }

    operator T*()
    {
        return object_;
    }

private:
    void release() throw()
    {
        R::release< T >( object_ );
    }
};

... y aquí está Releaser:

/*  Releaser
    Calls Release() on the object (for use with Finaliser). */
class Releaser
{
public:
    template< class T > static void release( T* object )
    {
        if (object != NULL)
        {
            object->Release();
        }
    }
};

Tengo algunos tipos diferentes de liberador como este, incluido uno gratis () y otro para CloseHandle ().

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top