2010-08-25 15 views
12

A veces se encuentra con código que tiene paréntesis de llaves adicionales, que no tienen nada que ver con el alcance, solo son para facilitar la lectura y evitar errores.Paréntesis de llaves adicionales en el código C++

Por ejemplo:

GetMutexLock(handle) ; 
{ 
    // brace brackets "scope" the lock, 
    // must close block/remember 
    // to release the handle. 
    // similar to C#'s lock construct 
} 
ReleaseMutexLock(handle) ; 

Otros lugares que he visto son:

glBegin(GL_TRIANGLES) ; 
{ 
    glVertex3d(..) ; 
    glVertex3d(..) ; 
    glVertex3d(..) ; 
} // must remember to glEnd! 
glEnd() ; 

Esto introduce un error de compilación si el mutex no es liberado (suponiendo que recordar tanto el} y el Release() llamada).

  1. ¿Es esta una mala práctica? ¿Por qué?
  2. Si no es uno, ¿podría cambiar la forma en que se compila el código o hacerlo más lento?
+2

En OpenGL, esta práctica está destinada a mejorar la legibilidad del código. Los corchetes en las llamadas glBegin() actúan como un delimitador visual del grupo de vértices que deberían dibujarse. – karlphillip

+0

¡Es una práctica terrible! El por qué alguien usaría delimitadores de bloque para hacer que un bloque de código se destaque está más allá de mí. Puedo imaginarme todas las micro-micro-optimizaciones que se pierden ...todo se sacrificó por algo tan al lado del punto como legibilidad. ¿A qué viene el mundo? –

+1

@Noah, le he cambiado el nombre a Sarcastic Cat. – bobobobo

Respuesta

30

Los refuerzos están bien, todo lo que hacen es limitar el alcance y no ralentizará nada. Se puede ver como más limpio. (Siempre es preferible un código limpio sobre el código rápido, si es limpio, no se preocupe por la velocidad hasta que perfil.)


Sin embargo, con respecto a los recursos que es una mala práctica, porque usted ha puesto a sí mismo en condiciones de filtrar un recurso. Si algo en el bloque arroja o regresa, bang estás muerto.

Uso Alcance unida a la Gestión de Recursos (SBRM, también conocido como RAII), lo que limita un recurso a un ámbito, utilizando el destructor:

class mutex_lock 
{ 
public: 
    mutex_lock(HANDLE pHandle) : 
    mHandle(pHandle) 
    { 
     //acquire resource 
     GetMutexLock(mHandle); 
    } 

    ~mutex_lock() 
    { 
     // release resource, bound to scope 
     ReleaseMutexLock(mHandle); 
    } 

private: 
    // resource 
    HANDLE mHandle; 

    // noncopyable 
    mutex_lock(const mutex_lock&); 
    mutex_lock& operator=(const mutex_lock&); 
}; 

Así se obtiene:

{ 
    mutex_lock m(handle); 
    // brace brackets "scope" the lock, 
    // AUTOMATICALLY 
} 

Haga esto todos los recursos, es más limpio y seguro. Si está en posición de decir "Necesito liberar este recurso", lo ha hecho mal; deben ser manejados automáticamente.

+7

+1 por corrección ... ¡y este nuevo acrónimo de SBRM que no conocía! Mucho mejor que RAII en cuanto a expresar el intento. ¡Y ni siquiera parece haber una página de wikipedia para él! –

3

No es una mala práctica. No hace nada más lento; es solo una forma de estructurar el código.

Hacer que el compilador haga la comprobación de errores & hacer cumplir para usted siempre es algo bueno.

+0

+1 para hacer que el compilador haga su trabajo ... –

+2

desafortunadamente, liberar el recurso usted mismo es propenso a errores ... –

1

Cualquier cosa que mejore la readablidad en mi humilde opinión es una buena práctica. Si agregar llaves ayuda con la legibilidad, ¡adelante!

La adición de llaves adicionales no cambiará la forma en que se compila el código. No hará que el funcionamiento del programa sea más lento.

+0

{I {think {that {adding {llaves {puede {reducir} legibilidad}}}}}} . En serio, las llaves me dicen "aquí hay una estructura de control". Cuando no hay ninguno, sigo buscándolo de todos modos por un momento. Causarme a pasar la cognición y una fracción de segundo buscando algo que no está allí no es legibilidad. –

+0

@David - No creo que OP haya pensado en agregar un número arbitrario de llaves al código como en su ejemplo. Me refería a la forma en que el OP usaba llaves. Obviamente, agregar llaves como en su ejemplo no ayuda con la legibilidad. – Starkey

+0

Usar apoyos para cualquier propósito que no sea indicar una estructura de control no ayuda con la legibilidad, en lo que a mí respecta. Esto no cuenta como una estructura de control. Usarlos para limitar el alcance de un recurso asignado por RAII es. –

17

Las abrazaderas afectan el alcance variable. Por lo que sé, eso es todo lo que hacen.

Sí, esto puede afectar la forma en que se compila el programa. Los destructores serán llamados al final del bloque en lugar de esperar hasta el final de la función.

A menudo esto es lo que quiere hacer. Por ejemplo, su GetMutexLock y ReleaseMutexLock sería mucho mejor código C++ escribe así:

struct MutexLocker { 
    Handle handle; 
    MutexLocker(handle) : handle(handle) { GetMutexLock(handle); } 
    ~MutexLocker() { ReleaseMutexLock(handle); }  
}; 
... 
{ 
    MutexLocker lock(handle); 
    // brace brackets "scope" the lock, 
    // must close block/remember 
    // to release the handle. 
    // similar to C#'s lock construct 
} 

El uso de este estilo más C++, el bloqueo se libera automáticamente al final del bloque. Se lanzará en todas las circunstancias, incluidas excepciones, con las excepciones de setjmp/longjmp o un bloqueo o aborto del programa.

+0

Este es el camino correcto a seguir. – Puppy

+12

Debe nombrar el casillero, o es solo un temporal que muere de inmediato. Y probablemente deba cambiar los comentarios después para que coincida con la nueva administración de recursos. – GManNickG

+0

La sintaxis correcta es 'MutexLocker handle;', no 'MutexLocker (handle);'. – kennytm

2

No hará ninguna diferencia en el código compilado, además de llamar a cualquier destructores al final de ese bloque en lugar de al final del bloque circundante, a menos que el compilador esté completamente loco.

Personalmente, lo llamaría una mala práctica; la forma de evitar el tipo de errores que puede cometer aquí es utilizar la administración de recursos con ámbito (a veces llamada RAII), no utilizar recordatorios tipográficos propensos a errores. Escribiría el código como algo similar a

{ 
    mutex::scoped_lock lock(mutex); 
    // brace brackets *really* scope the lock 
} // scoped_lock destructor releases the lock 

{ 
    gl_group gl(GL_TRIANGLES); // calls glBegin() 
    gl.Vertex3d(..); 
    gl.Vertex3d(..); 
    gl.Vertex3d(..); 
} // gl_group destructor calls glEnd() 
1

Esto es mucho más útil (en mi humilde opinión) en C++ con destructores de objetos; los ejemplos son en C.

Imagínese si usted hizo una clase MutexLock:

class MutexLock { 
private: 
    HANDLE handle; 
public: 
    MutexLock() : handle(0) { 
     GetMutexLock(handle); 
    } 

    ~MutexLock() { 
     ReleaseMutexLock(handle); 
    } 
} 

entonces se podría bloquear el alcance que sólo el código que necesitaba proporcionando un ámbito nuevo con las llaves:

1

Si está poniendo código en llaves, probablemente debería dividirlo en su propio método. Si se trata de una sola unidad discreta, ¿por qué no etiquetarla y dividirla funcionalmente? Eso hará que sea explícito lo que hace el bloque, y las personas que luego lean el código no tendrán que descubrirlo.

3

La ubicación específica de { ... } en su ejemplo original sirve simplemente como formateo de azúcar al hacerlo más obvio donde comienza un grupo de declaraciones lógicamente relacionadas y dónde termina. Como se muestra en los ejemplos, no tiene efecto en el código compilado.

No sé a qué te refieres con "esto presenta un error de compilación si no se libera el mutex". Eso simplemente no es verdad. Tal uso de { ... } no puede y no introducirá ningún error de compilación.

Si se trata de una buena práctica es una cuestión de preferencia personal. Se ve bien Alternativamente, puede usar comentarios y/o sangrado para indicar la agrupación lógica de las declaraciones en el código, sin ningún { ... } adicional.

Existen varias técnicas basadas en el alcance, algunas de las cuales se han ilustrado con las otras respuestas aquí, pero lo que tienes en tu OP ni remotamente parece algo así. Una vez más, lo que tiene en su OP (como se muestra) es puramente un hábito de formato de fuente con { ... } superfluo que no tiene ningún efecto en el código generado.

+0

Parece que eres el único que ha comentado sobre el problema del "error del compilador", bien observado. Mi impresión es que el cartel ha visto macros utilizadas como esto: BEGIN_X // hacer cosas END_X donde el BEGIN_X y sustituciones END_X incluyen su propia {y}, tal que una falta END_X sí produce un error de compilación. Cosas feas –

+0

@Tony: Boost tiene algunos de esos. Una vez obtuve alrededor de cien páginas de errores porque olvidé uno. "Cosas feas" no comienza a describirlo :) –

Cuestiones relacionadas