¿Usar GOTO o no?


Actualmente estoy trabajando en un proyecto donde las declaraciones goto se utilizan de forma intensa. El propósito principal de las sentencias goto es tener una sección de limpieza en una rutina en lugar de varias sentencias de retorno. Como abajo:

BOOL foo()
{
   BOOL bRetVal = FALSE;
   int *p = NULL;

   p = new int;
   if (p == NULL)
   {
     cout<<" OOM \n";
     goto Exit;
   }

   // Lot of code...

Exit:
   if(p)
   {
     delete p;
     p = NULL;
   }
   return bRetVal;
}

Esto hace que sea mucho más fácil, ya que podemos rastrear nuestro código de limpieza en una sección del código, es decir, después de la etiqueta de salida.

Sin embargo, he leído muchos lugares es mala práctica tener declaraciones goto.

Actualmente estoy leyendo el Código Completa libro, y dice que necesitamos usar variables cercanas a sus declaraciones. Si usamos goto entonces necesitamos declarar / inicializar todas las variables antes del primer uso de goto de lo contrario el compilador dará errores que la inicialización de la variable xx es omitida por la sentencia goto.

¿Qué camino es correcto?


Del comentario de Scott:

Parece que usar goto para saltar de una sección a otra es malo, ya que hace que el código sea difícil de leer y entender.

Pero si usamos goto solo para avanzar y para una etiqueta, entonces debería estar bien(?).

Author: Alien01, 2008-12-18

30 answers

No estoy seguro de lo que quiere decir con limpiar código, pero en C++ hay un concepto llamado "la adquisición de recursos es la inicialización" y debería ser responsabilidad de sus destructores limpiar las cosas.

(Tenga en cuenta que en C# y Java, esto generalmente se resuelve por try/finally)

Para más información echa un vistazo a esta página: http://www.research.att.com/~bs / bs_faq2.html#finalmente

EDIT : Permítanme aclarar esto un poco.

Considere la código siguiente:

void MyMethod()
{
    MyClass *myInstance = new MyClass("myParameter");
    /* Your code here */
    delete myInstance;
}

El problema: ¿Qué sucede si tiene múltiples salidas de la función? Usted tiene que realizar un seguimiento de cada salida y eliminar sus objetos en todas las salidas posibles! De lo contrario, tendrá fugas de memoria y recursos zombie, ¿verdad?

La solución : Use referencias de objetos en su lugar, ya que se limpian automáticamente cuando el control sale del ámbito.

void MyMethod()
{
    MyClass myInstance("myParameter");
    /* Your code here */
    /* You don't need delete - myInstance will be destructed and deleted
     * automatically on function exit */
}

Oh sí, y usar std::unique_ptr o algo similar porque el el ejemplo anterior, tal como es, es obviamente imperfecto.

 58
Author: Tamas Czinege,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-27 14:23:12

Nunca he tenido que usar un goto en C++. Nunca. NUNCA. Si hay una situación que debe ser utilizado, es increíblemente raro. Si realmente estás considerando hacer de goto una parte estándar de tu lógica, algo se ha salido de las pistas.

 59
Author: Gene Roberts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 21:03:08

Básicamente hay dos puntos que la gente está haciendo con respecto a gotos y su código:

  1. Goto es malo. Es muy raro encontrar un lugar donde necesitas gotos, pero no sugeriría golpearlo completamente. Aunque C++ tiene un flujo de control lo suficientemente inteligente como para hacer que goto rara vez sea apropiado.

  2. Su mecanismo de limpieza es incorrecto: Este punto es mucho más importante. En C, usar la administración de memoria por su cuenta no solo está bien, sino que a menudo la mejor manera de hacer las cosas. En C++, su objetivo debe ser evitar la administración de memoria tanto como sea posible. Debe evitar la administración de memoria tanto como sea posible. Deja que el compilador lo haga por ti. En lugar de usar new, simplemente declare variables. El único momento en que realmente necesitará administración de memoria es cuando no sepa el tamaño de sus datos de antemano. Incluso entonces, debería intentar simplemente usar algunas de las colecciones STL en su lugar.

En el caso de que usted legítimamente necesita memoria administración (realmente no ha proporcionado ninguna evidencia de esto), entonces debe encapsular su administración de memoria dentro de una clase a través de constructores para asignar memoria y deconstructores para desasignar memoria.

Su respuesta de que su forma de hacer las cosas es mucho más fácil no es realmente cierta a largo plazo. En primer lugar, una vez que tenga una idea fuerte de C++ haciendo tales constructores será 2nd nature. Personalmente, me parece más fácil usar constructores que usar código de limpieza, ya que no tengo necesidad de preste mucha atención para asegurarse de que estoy desasignando correctamente. En su lugar, puedo dejar que el objeto deje el ámbito y el lenguaje lo maneje por mí. Además, mantenerlos es mucho más fácil que mantener una sección de limpieza y mucho menos propenso a problemas.

En resumen, goto puede ser una buena opción en algunas situaciones pero no en esta. Aquí es solo pereza a corto plazo.

 22
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-10 08:09:00

Su código es extremadamente no idiomático y nunca debe escribirlo. Básicamente estás emulando C en C++ allí. Pero otros han comentado sobre eso, y han señalado a RAII como la alternativa.

Sin embargo, tu código no funcionará como esperas, porque esto:

p = new int;
if(p==NULL) { … }

No nunca evaluará a true (excepto si has sobrecargado operator new de una manera extraña). Si operator new no puede asignar suficiente memoria, lanza una excepción, nunca, siempre devuelve 0, al menos no con este conjunto de parámetros; hay una colocación especial-nueva sobrecarga que toma una instancia de tipo std::nothrow y que de hecho devuelve 0 en lugar de lanzar una excepción. Pero esta versión rara vez se usa en código normal. Algunos códigos de bajo nivel o aplicaciones de dispositivos incrustados podrían beneficiarse de ella en contextos donde lidiar con excepciones es demasiado costoso.

Algo similar es cierto para su delete bloque, como Harald como dijo: if (p) es innecesario en frente de delete p.

Además, no estoy seguro de si su ejemplo fue elegido intencionalmente porque este código se puede reescribir de la siguiente manera:

bool foo() // prefer native types to BOOL, if possible
{
    bool ret = false;
    int i;
    // Lots of code.
    return ret;
}
 20
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 21:14:29

Probablemente no es una buena idea.

 16
Author: Marc Charbonneau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 20:53:10

En general, y en la superficie, no hay nada malo con su enfoque, siempre que solo tenga una etiqueta, y que los goto siempre sigan adelante. Por ejemplo, este código:

int foo()
{
    int *pWhatEver = ...;
    if (something(pWhatEver))
    { 
        delete pWhatEver;
        return 1;
    }
    else
    {
        delete pWhatEver;
        return 5;
    }
}

Y este código:

int foo()
{
    int ret;
    int *pWhatEver = ...;
    if (something(pWhatEver))
    { 
        ret = 1;
        goto exit;
    }
    else
    {
        ret = 1;
        goto exit;
    }
exit:
    delete pWhatEver;
    return ret;
}

Realmente no son tan diferentes entre sí. Si puedes aceptar una, deberías poder aceptar la otra.

Sin embargo, en muchos casos el patrón RAII (la adquisición de recursos es la inicialización) puede hacer que el código sea mucho más limpio y más mantenible. Por ejemplo, este código:

int foo()
{
    Auto<int> pWhatEver = ...;

    if (something(pWhatEver))
    {
        return 1;
    }
    else
    {
        return 5;
    }
}

Es más corto, más fácil de leer y más fácil de mantener que los dos ejemplos anteriores.

Por lo tanto, recomendaría usar el enfoque RAII si puede.

 11
Author: Scott Wisniewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:03:41

Su ejemplo no es seguro para excepciones.

Si está utilizando goto para limpiar el código, entonces, si ocurre una excepción antes del código de limpieza, se pierde por completo. Si afirma que no utiliza excepciones, entonces se equivoca porque el new lanzará bad_alloc cuando no tenga suficiente memoria.

También en este punto (cuando se lanza bad_alloc), su pila se desenrollará, faltando todo el código de limpieza en cada función en el camino hacia la pila de llamadas, por lo tanto, no se limpia su código.

Usted necesita mirar para hacer una investigación en punteros inteligentes. En la situación anterior solo podría usar un std::auto_ptr<>.

También tenga en cuenta que en el código C++ no hay necesidad de verificar si un puntero es NULO (generalmente porque nunca tiene punteros RAW), sino porque new no devolverá NULL (arroja).

También en C++ a diferencia de (C) es común ver retornos tempranos en el código. Esto se debe a que RAII hará la limpieza automáticamente, mientras que en el código C debe hacer asegúrese de agregar un código especial de limpieza al final de la función (un poco como su código).

 8
Author: Loki Astari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:11:58

Creo que otras respuestas (y sus comentarios) han cubierto todos los puntos importantes, pero aquí hay una cosa que aún no se ha hecho correctamente:

Cómo debería ser tu código:

bool foo() //lowercase bool is a built-in C++ type. Use it if you're writing C++.
{
  try {
    std::unique_ptr<int> p(new int);
    // lots of code, and just return true or false directly when you're done
  }
  catch (std::bad_alloc){ // new throws an exception on OOM, it doesn't return NULL
    cout<<" OOM \n";
    return false;
  }
}

Bueno, es más corto, y por lo que puedo ver, más correcto (maneja el caso OOM correctamente), y lo más importante, no tuve que escribir ningún código de limpieza o hacer nada especial para "asegurarse de que mi valor de retorno se inicializa".

Un problema con su código solo me di cuenta cuando escribí esto, es " ¿cuál es el valor de bRetVal en este momento?". No lo sé porque, fue declarado waaaaay arriba, y fue asignado por última vez a cuándo? En algún punto por encima de esto. Tengo que leer a través de toda la función para asegurarse de que entiendo lo que va a ser devuelto.

¿Y cómo me convenzo de que la memoria se libera?

¿Cómo sé que nunca olvidamos saltar a la etiqueta de limpieza? Tengo que trabajar hacia atrás desde la etiqueta de limpieza, encontrando cada goto que señala a ella, y lo más importante, encontrar los que no están allí. Necesito rastrear a través de todas las rutas de la función solo para estar seguro de que la función se limpia correctamente. Eso me parece un código espagueti.

Código muy frágil, porque cadavez que un recurso tiene que ser limpiado tienes que recordar para duplicar tu código de limpieza. ¿Por qué no escribirlo una vez, en el tipo que necesita ser limpiado? Y luego confiar en que es ejecutado automáticamente, cada vez que lo necesitamos?

 8
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-04 23:41:05

Como se usa en el kernel de Linux, los goto usados para la limpieza funcionan bien cuando una sola función debe realizar 2 o más pasos que pueden necesitar ser deshechos. Los pasos no necesitan ser asignación de memoria. Podría ser un cambio en la configuración de un fragmento de código o en un registro de e/S chipset. Los Goto solo deben ser necesarios en un pequeño número de casos, pero a menudo cuando se usan correctamente, pueden ser la mejor solución. No son malvados. Son una herramienta.

En lugar de...

do_step1;
if (failed)
{
  undo_step1;
  return failure;
}

do_step2;
if (failed)
{
  undo_step2;
  undo_step1;
  return failure;
}

do_step3;
if (failed)
{
  undo_step3;
  undo_step2;
  undo_step1;
  return failure;
}

return success;

Usted puede hacer lo mismo con las declaraciones goto como esta:

do_step1;
if (failed) goto unwind_step1;

do_step2;
if (failed) goto unwind_step2;

do_step3;
if (failed) goto unwind_step3;

return success;

unwind_step3:
  undo_step3;

unwind_step2:
  undo_step2;

unwind_step1:
  undo_step1;

return failure;

Debe quedar claro que dados estos dos ejemplos, uno es preferible al otro. En cuanto a la multitud RAII... No hay nada malo con ese enfoque, siempre y cuando puedan garantizar que el desenrollado siempre ocurrirá en exactamente el orden inverso: 3, 2, 1. Y por último, algunas personas no usan excepciones en su código e instruyen a los compiladores para deshabilitarlas. Por lo tanto, no todo el código debe ser seguro para excepciones.

 6
Author: Harvey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-22 19:10:22

Debe leer este resumen de hilo de las listas de correo del núcleo Linux (prestando especial atención a las respuestas de Linus Torvalds) antes de formar una política para goto:

Http://kerneltrap.org/node/553/2131

 6
Author: too much php,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:16:02

En los ocho años que he estado programando he usado mucho goto, la mayor parte de eso fue en el primer año cuando estaba usando una versión de GW-BASIC y un libro de 1980 que no dejaba claro que goto solo debería usarse en ciertos casos. La única vez que he usado goto en C++ es cuando tenía código como el siguiente, y no estoy seguro de si había una mejor manera.

for (int i=0; i<10; i++) {
    for (int j=0; j<10; j++)
    {
        if (somecondition==true)
        {
            goto finish;
        }
        //Some code
    }
    //Some code
}
finish:

La única situación que conozco donde goto todavía se usa mucho es el lenguaje ensamblador de mainframe, y los programadores I asegúrese de documentar dónde está saltando el código y por qué.

 6
Author: Jared,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:35:38

En general, debe diseñar sus programas para limitar la necesidad de gotos. Utilice técnicas OO para "limpiar" sus valores de retorno. Hay maneras de hacer esto, que no requieren el uso de gotos o complicar el código. Hay casos en los que los goto son muy útiles (por ejemplo, ámbitos profundamente anidados), pero si es posible deben evitarse.

 5
Author: Marcin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 21:08:00

La desventaja de GOTO está bastante bien discutida. Solo agregaría que 1) a veces tienes que usarlos y debes saber cómo minimizar los problemas, y 2) algunas técnicas de programación aceptadas son GOTO-in-disguise, así que ten cuidado.

1) Cuando tienes que usar GOTO, como en ASM o en .archivos bat, piensa como un compilador. Si desea codificar

 if (some_test){
  ... the body ...
}

Haga lo que hace un compilador. Generar una etiqueta cuyo propósito es saltar sobre el cuerpo, no hacer lo que sigue. es decir,

 if (not some_test) GOTO label_at_end_of_body
  ... the body ...
label_at_end_of_body:

No

 if (not some_test) GOTO the_label_named_for_whatever_gets_done_next
  ... the body ...

the_label_named_for_whatever_gets_done_next:

En otras palabras, el propósito de la etiqueta no es hacer algo, sino saltar algo.

2) Lo que yo llamo GOTO-in-disguise es cualquier cosa que podría convertirse en código GOTO+LABELS simplemente definiendo un par de macros. Un ejemplo es la técnica de implementar autómatas de estado finito al tener una variable de estado y una sentencia while-switch.

 while (not_done){
    switch(state){
        case S1:
            ... do stuff 1 ...
            state = S2;
            break;
        case S2:
            ... do stuff 2 ...
            state = S1;
            break;
        .........
    }
}

Puede convertirse en:

 while (not_done){
    switch(state){
        LABEL(S1):
            ... do stuff 1 ...
            GOTO(S2);
        LABEL(S2):
            ... do stuff 2 ...
            GOTO(S1);
        .........
    }
}

Simplemente definiendo una pareja macro. Casi cualquier FSA se puede convertir en código estructurado sin goto. Prefiero mantenerme alejado del código GOTO-in-disguise porque puede entrar en los mismos problemas de código spaghetti que los goto no disimulados.

Agregado: Solo para tranquilizar: Creo que una marca de un buen programador es reconocer cuando las reglas comunes no se aplican.

 5
Author: Mike Dunlavey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-22 20:15:23

Goto proporciona mejor no te repitas (SECO) cuando la "lógica-final-de-cola" es común a algunos-pero-no-todos-los-casos. Especialmente dentro de una declaración de" switch", a menudo uso goto cuando algunas de las ramas de switch tienen puntos en común.

switch(){
   case a:  ... goto L_abTail;
   case b: ... goto L_abTail;
L_abTail: <commmon stuff>
    break://end of case b
case c:
.....
}//switch

Probablemente haya notado que la introducción de llaves adicionales es suficiente para satisfacer al compilador cuando necesita tal fusión final en el medio de una rutina. En otras palabras, no es necesario declarar todo en la parte superior; eso es legibilidad inferior de hecho.

...
   goto L_skipMiddle;
{
    int declInMiddleVar = 0;
    ....
}
L_skipMiddle: ;

Con las versiones posteriores de Visual Studio detectando el uso de variables no inicializadas, me encuentro siempre inicializando la mayoría de las variables a pesar de que creo que pueden estar asignadas en todas las ramas - es fácil codificar una sentencia "tracing" que refs una variable que nunca fue asignada porque su mente no piensa en la sentencia tracing como "código real", pero por supuesto Visual Studio todavía detectará un error.

Además no repítete a ti mismo, asignando nombres de etiqueta a tal lógica de cola incluso parece ayudar a mi mente a mantener las cosas claras al elegir nombres de etiqueta agradables. Sin una etiqueta significativa, tus comentarios podrían terminar diciendo lo mismo.

Por supuesto, si realmente está asignando recursos, entonces si auto-ptr no encaja, realmente debe usar un try-catch, pero tail-end-merge-don't-repeat-yourself sucede muy a menudo cuando la seguridad de excepción no es un problema.

En resumen, mientras que goto se puede usar para codificar estructuras como espaguetis, en el caso de una secuencia final que es común a algunos-pero-no-todos-los-casos, entonces el goto MEJORA la legibilidad del código e incluso la mantenibilidad si de lo contrario estaría copiando/pegando cosas para que mucho más tarde alguien pueda actualizar uno-y-no-el-otro. Así que es otro caso donde ser fanático de un dogma puede ser contraproducente.

 5
Author: pngaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:21:30

Usar goto para ir a una sección de limpieza va a causar muchos problemas.

Primero, las secciones de limpieza son propensas a problemas. Tienen baja cohesión (ningún papel real que se pueda describir en términos de lo que el programa está tratando de hacer ), alto acoplamiento (la corrección depende en gran medida de otras secciones del código), y no son en absoluto a salvo de excepciones. Mira si puedes usar destructores para limpiar. Por ejemplo, si int *p se cambia a auto_ptr<int> p, lo que p apunta será automáticamente lanzar.

En segundo lugar, como usted señala, va a obligarle a declarar variables mucho antes de su uso, lo que hará que sea más difícil entender el código.

En tercer lugar, mientras que usted está proponiendo un uso bastante disciplinado de goto, va a haber la tentación de usarlos de una manera más flexible, y entonces el código será difícil de entender.

Hay muy pocas situaciones donde un goto es apropiado. La mayoría de las veces, cuando estás tentado a usarlos, es una señal de que estás haciendo las cosas mal.

 4
Author: David Thornley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 20:48:44

Dado que este es un tema clásico, responderé con la declaración de Dijkstra Go-to considered harmful (publicada originalmente en ACM).

 4
Author: mstrobl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:04:42

Todo el propósito del modismo every-function-has-a-single-exit-point en C era poner todas las cosas de limpieza en un solo lugar. Si usa destructores C++ para manejar la limpieza, eso ya no es necesario clean la limpieza se hará independientemente de cuántos puntos de salida tenga una función. Así que en el código C++ correctamente diseñado, ya no hay necesidad de este tipo de cosas.

 3
Author: Head Geek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 21:01:31

Mucha gente enloquece con goto son malvados; no lo son. Dicho esto, nunca necesitarás uno; casi siempre hay una mejor manera.

Cuando me encuentro "necesitando" un goto para hacer este tipo de cosas, casi siempre encuentro que mi código es demasiado complejo y se puede dividir fácilmente en algunas llamadas a métodos que son más fáciles de leer y tratar. Su código de llamada puede hacer algo como:

// Setup
if(
     methodA() &&
     methodB() &&
     methodC()
 )
 // Cleanup

No es que esto sea perfecto, pero es mucho más fácil de seguir ya que todos sus métodos se nombrará para indicar claramente cuál podría ser el problema.

La lectura de los comentarios, sin embargo, debe indicar que su equipo tiene problemas más urgentes que el manejo de goto.

 3
Author: Bill K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:13:48

El código que nos está dando es (casi) código C escrito dentro de un archivo C++. El tipo de limpieza de memoria que está utilizando estaría bien en un programa de C que no utiliza código/bibliotecas de C++.

En C++, su código es simplemente inseguro y poco fiable. En C++ el tipo de gestión que está pidiendo se hace de manera diferente. Utilice constructores / destructores. Usa punteros inteligentes. Usa la pila. En una palabra, use RAII .

Su código podría (es decir, en C++, DEBERÍA) escribirse como:

BOOL foo()
{
   BOOL bRetVal = FALSE;

   std::auto_ptr<int> p = new int;

   // Lot of code...

   return bRetVal ;
}

(Tenga en cuenta que new-ing un int es algo tonto en código real, pero puede reemplazar int por cualquier tipo de objeto, y entonces, tiene más sentido). Imaginemos que tenemos un objeto de tipo T (T podría ser un int, alguna clase C++, etc.). Entonces el código se convierte en:

BOOL foo()
{
   BOOL bRetVal = FALSE;

   std::auto_ptr<T> p = new T;

   // Lot of code...

   return bRetVal ;
}

O incluso mejor, usando la pila:

BOOL foo()
{
   BOOL bRetVal = FALSE;

   T p ;

   // Lot of code...

   return bRetVal;
}

De todos modos, cualquiera de los ejemplos anteriores son magnitudes más fáciles de leer y seguras que su ejemplo.

RAII tiene muchas facetas (es decir, el uso de punteros inteligentes, la pila, el uso de vectores en lugar de arrays de longitud variable, etc.), pero en general se trata de escribir el menor código posible, dejando que el compilador limpie las cosas en el momento adecuado.

 2
Author: paercebal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:23:48

Las únicas dos razones por las que uso goto en mi código C++ son:

  • Romper un nivel 2 + bucles anidados
  • Flujos complicados como este (un comentario en mi programa):

    /* Analysis algorithm:
    
      1.  if classData [exporter] [classDef with name 'className'] exists, return it,
          else
      2.    if project/target_codename/temp/classmeta/className.xml exist, parse it and go back to 1 as it will succeed.
      3.    if that file don't exists, generate it via haxe -xml, and go back to 1 as it will succeed.
    
    */
    

Para la legibilidad del código aquí, después de este comentario, definí la etiqueta step1 y la usé en los pasos 2 y 3. En realidad, en más de 60 archivos fuente, solo esta situación y uno de 4 niveles anidados son los lugares que usé goto. Sólo dos lugares.

 2
Author: Петър Петров,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:41:17

Todo lo anterior es válido, es posible que también desee ver si puede reducir la complejidad de su código y aliviar la necesidad de goto reduciendo la cantidad de código que está en la sección marcada como "mucho código" en su ejemplo. Adicionalmente delete 0 es una sentencia válida de C++

 1
Author: Harald Scheirich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 20:55:12

Usar etiquetas GOTO en C++ es una mala manera de programar, puede reducir la necesidad haciendo programación OO (deconstructores!) y tratando de mantener los procedimientos tan pequeños como sea posible.

Su ejemplo se ve un poco raro, no hay necesidad de eliminar un puntero NULO. Y hoy en día se lanza una excepción cuando un puntero no se puede asignar.

Su procedimiento podría escribirse como:

bool foo()
{
    bool bRetVal = false;
    int p = 0;

    // Calls to various methods that do algorithms on the p integer
    // and give a return value back to this procedure.

    return bRetVal;
}

Debe colocar un bloque try catch en el manejo principal del programa problemas de memoria que informa al usuario sobre la falta de memoria , que es muy raro... (¿No informa el propio sistema operativo sobre esto también?)

También tenga en cuenta que no siempre es necesario usar un puntero , solo son útiles para cosas dinámicas. (Crear una cosa dentro de un método que no depende de la entrada desde cualquier lugar no es realmente dinámico)

 1
Author: Tom Wijsman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 21:06:24

No voy a decir que goto siempre es malo, pero su uso de ella sin duda lo es. Ese tipo de" secciones de limpieza " era bastante común a principios de la década de 1990, pero usarlo para el nuevo código es puro mal.

 1
Author: Nemanja Trifunovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 21:41:09

La forma más fácil de evitar lo que estás haciendo aquí es poner toda esta limpieza en algún tipo de estructura simple y crear una instancia de ella. Por ejemplo, en lugar de:

void MyClass::myFunction()
{
   A* a = new A;
   B* b = new B;
   C* c = new C;
   StartSomeBackgroundTask();
   MaybeBeginAnUndoBlockToo();

   if ( ... )
   {
     goto Exit;
   }

   if ( ... ) { .. }
   else
   {
      ... // what happens if this throws an exception??? too bad...
      goto Exit;
   }

Exit:
  delete a;
  delete b;
  delete c;
  StopMyBackgroundTask();
  EndMyUndoBlock();
}

Deberías hacer esta limpieza de alguna manera como:

struct MyFunctionResourceGuard
{
  MyFunctionResourceGuard( MyClass& owner ) 
  : m_owner( owner )
  , _a( new A )
  , _b( new B )
  , _c( new C )
  {
      m_owner.StartSomeBackgroundTask();
      m_owner.MaybeBeginAnUndoBlockToo();
  }

  ~MyFunctionResourceGuard()
  {
     m_owner.StopMyBackgroundTask();
     m_owner.EndMyUndoBlock();
  }

  std::auto_ptr<A> _a;
  std::auto_ptr<B> _b;
  std::auto_ptr<C> _c;

};

void MyClass::myFunction()
{
   MyFunctionResourceGuard guard( *this );

   if ( ... )
   {
     return;
   }

   if ( ... ) { .. }
   else
   {
      ...
   }
}
 1
Author: Michel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 21:46:59

Hace unos años se me ocurrió un pseudo-idioma que evita goto, y es vagamente similar a hacer el manejo de excepciones en C. Probablemente ya ha sido inventado por alguien más, así que supongo que "lo descubrí de forma independiente":)

BOOL foo()
{
   BOOL bRetVal = FALSE;
   int *p=NULL;

   do
   {
       p = new int;
       if(p==NULL)
       {
          cout<<" OOM \n";
          break;
       }

       // Lot of code...

       bRetVal = TRUE;

    } while (false);

   if(p)
   {
     delete p;
     p= NULL;
   }

   return bRetVal;
}
 1
Author: ggambett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-20 14:56:56

Creo que usar el goto para el código de salida es malo, ya que hay muchas otras soluciones con baja sobrecarga, como tener una función de salida y devolver el valor de la función de salida cuando sea necesario. Normalmente en las funciones miembro, sin embargo, esto no debería ser necesario, de lo contrario esto podría ser una indicación de que hay un poco de exceso de código sucediendo.

Normalmente, la única excepción que hago de la regla" no goto " cuando se programa es cuando se rompe de bucles anidados a un nivel específico, que he solo se encontró con la necesidad de hacer cuando se trabaja en la programación matemática.

Por ejemplo:

for(int i_index = start_index; i_index >= 0; --i_index)
{
    for(int j_index = start_index; j_index >=0; --j_index)
        for(int k_index = start_index; k_index >= 0; --k_index)
            if(my_condition)
                goto BREAK_NESTED_LOOP_j_index;
BREAK_NESTED_LOOP_j_index:;
}
 1
Author: Hazok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-11 22:30:33

Ese código tiene un montón de problemas, la mayoría de los cuales ya se han señalado, por ejemplo:

  • La función es demasiado larga; refactorizar algún código en funciones separadas podría ayudar.

  • El uso de punteros cuando las instancias normales probablemente funcionará bien.

  • No aprovechar STL tipos como auto_ptr

  • Comprobación incorrecta de errores y no detectar excepciones. (Yo diría que la comprobación de OOM es sin sentido en la gran mayoría de plataformas, ya que si se queda sin memoria tiene problemas más grandes de lo que su software puede arreglar, a menos que esté escribiendo el sistema operativo en sí)

Nunca he necesitado un goto, y siempre he encontrado que usar goto es un síntoma de un conjunto mayor de problemas. Su caso no parece ser una excepción.

 1
Author: metao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 06:15:41

Usar "GOTO" cambiará las "lógicas" de un programa y cómo se interpreta o cómo se imagina que funcionaría.

Evitar los comandos GOTO siempre ha funcionado para mí, así que adivina cuando crees que podrías necesitarlo, todo lo que tal vez necesites es un rediseño.

Sin embargo, si miramos esto en un nivel Assmebly, jusing "jump" es como usar GOTO y eso se usa todo el tiempo, PERO, en Assembly puedes limpiar lo que sabes que tienes en la pila y otros registros antes de pasar en.

Así que, al usar GOTO, me aseguraría de que el software "apareciera" como los codificadores enterpret, GOTO tendrá un efecto "malo" en su software imho.

Así que esto es más una explicación de por qué no usar GOTO y no una solución para un reemplazo, porque eso depende mucho de cómo se construye todo lo demás.

 0
Author: Filip Ekberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 21:02:30

Puede que haya pasado algo por alto: saltas a la etiqueta Exit si P es null, luego pruebas para ver si no es null (que no lo es) para ver si necesitas eliminarlo (que no es necesario porque nunca fue asignado en primer lugar).

El if/goto no, y no necesita eliminar p. Reemplazar el goto con un retorno falso tendría el mismo efecto (y luego podría eliminar la etiqueta de salida).

Los únicos lugares que conozco donde los goto son útiles están enterrados profundamente en desagradables analizadores (o analizadores léxicos), y en la falsificación de máquinas de estado (enterrados en una masa de macros CPP). En esos dos casos se han utilizado para simplificar la lógica muy retorcida, pero eso es muy raro.

Las funciones (A llama A'), Try/Catches y setjmp/longjmps son formas más agradables de evitar un problema de sintaxis difícil.

Paul.

 0
Author: Paul W Homer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 23:19:31

Ignorando el hecho de que new nunca devolverá NULL, tome su código:

  BOOL foo()
  {
     BOOL bRetVal = FALSE;

     int *p=NULL;

     p = new int;

     if(p==NULL)
     {
        cout<<" OOM \n";
        goto Exit;
     }

     // Lot of code...

  Exit:
     if(p)
     {
        delete p;
        p= NULL;
     }

     return bRetVal;
  }

Y escríbelo así:

  BOOL foo()
  {
     BOOL bRetVal = FALSE;

     int *p = new int;

     if (p!=NULL)
     {
        // Lot of code...

        delete p;
     }
     else
     {
        cout<<" OOM \n";
     }

     return bRetVal;
  }
 0
Author: jussij,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-18 23:38:07