2010-03-05 6 views
9

Al leer proggit hoy, me encontré con este comentario en un post acerca de cómo los principales lugares en el desafío de Google Ai fueron tomados por C++. Usuario reventlov declaraPreguntas básicas sobre RAII, STL pop y PIMPL

El mayor problema que tengo con C++ es que es waaay demasiado fácil pensar que usted es un "programador de C++" sin comprender realmente todo lo que necesita para comprender el uso de C++ aceptablemente bien.

Debe conocer RAII, saber usar espacios de nombres y comprender el manejo de excepciones adecuado (por ejemplo, debería poder explicar por qué los métodos pop() en el STL no devuelven los valores que eliminan) . Debe saber cuál de las tres generaciones de funciones en la biblioteca estándar es la correcta. Debe estar familiarizado con conceptos como PIMPL. Debe comprender cómo funciona el diseño de la biblioteca estándar (especialmente el STL). Debe comprender cómo las macros interactúan con espacios de nombres, y por qué generalmente no debería usar macros en C++, y qué debería usar en su lugar (generalmente plantillas o líneas, rara vez una clase). Necesitas saber sobre el impulso.

Creo que soy uno de esos programadores C++ despistados que menciona. Para mantener este breve, mis preguntas son

  1. Puede dar un ejemplo de un error típico de supervisión de la RAII, p. donde las mejores prácticas dictan el uso de RAII pero los programadores han implementado usando de otra manera?
  2. ¿Por qué no los métodos pop() en STL devuelven el valor que eliminan?
  3. Leí la entrada de Wikipedia para PIMPL, no entendí nada de eso. ¿Puedes dar un ejemplo del uso típico del idioma PIMPL?
+0

No entiendo la pregunta 1, ¿puede usted reformularla? –

+21

¿Tal vez sería más feliz quedarse en reddit? La regla aquí es una pregunta a la vez. –

+2

@Neil, jajaja esto se convertirá en su próximo comentario de 10+ xD –

Respuesta

9
  1. Un buen ejemplo en el que RAII es crucial, pero a veces se olvida cuando se bloquea un mutex. Si tiene una sección de código que bloquea un mutex, realiza operaciones y luego lo desbloquea, si las operaciones generan una excepción o hacen que el hilo muera, el mutex permanece bloqueado. Esta es la razón por la cual hay varias clases de cerradura de ámbito (como QMutexLocker), ya que como se indica here, se le garantiza que se ejecutará el destructor. Por lo tanto, si usa un bloqueo de alcance, siempre se desbloqueará en caso de destrucción para evitar un bloqueo muerto.

  2. Pop devuelve void por el bien de la velocidad: SGI FAQ, y para evitar excepciones que puedan ser arrojadas por el constructor de copia de objetos.

  3. PIMPL es usado en gran medida por el framework Qt para proporcionar compatibilidad binaria. Le permite ocultar todas las partes internas de una estructura de datos de la API pública. Esto significa que, si desea agregar miembros privados a una clase, la agrega al puntero d. Esto mantiene Binary Code Compatibility ya que el único miembro de datos expuesto es un puntero.

+2

+1 para el punto 2: la decisión de SGI al diseñar el STL no se explicó en términos de excepción-seguridad, sino de eficiencia. Por supuesto, eso no muestra si el proceso de estandarización de C++ retuvo el mismo razonamiento, o llegó a la misma conclusión de otra manera. –

3

Lea "Exceptional C++" y "Exceptional C++ Style" de Herb Sutter.

+0

Estaba a punto de decir eso. "Excepcional C++" cubre estos tres temas (y otros) muy a fondo. –

9

Responderé el punto 2 y dejaré el resto para otros. La razón principal por la cual pop no devuelve el valor eliminado se debe a la seguridad de la excepción.

Primero, comprenda que los contenedores C++ (a diferencia, por ejemplo, los de Java) tienen sus objetos por valor. Eso significa que si desea que el contenedor devuelva el objeto en el momento del estallido, tiene que devolver ese objeto por valor, copiando el objeto que se va a eliminar. Por el contrario, al tener acceso a top antes de pop, simplemente puede devolver una referencia al elemento superior, y puede copiarlo en el contenido de su corazón antes de abrirlo. (Considerando que, si pop devolvió el elemento de referencia, que sería una referencia colgando, ya que el objeto ya no está en el contenedor.)

La consecuencia de hacer pop retorno por valor (aparte de la ineficiencia que participan en la copia el objeto que se elimina) es que pone en peligro la seguridad de las excepciones. Idealmente, si una operación arroja una excepción, el estado de los objetos involucrados no cambia. Pero si el pop devolviera el objeto eliminado por valor, ¿qué pasaría si el constructor de copia para ese objeto fallara? El objeto ya se habría eliminado del contenedor y, por lo tanto, el estado ya ha cambiado.

Esto es más prolijo de lo que quería, pero espero que te dé una idea de por qué pop devolver un valor es una mala idea.

+2

Esto no explica por qué otras funciones de mutador en la biblioteca estándar devuelven tipos no integrados por valor (por cierto, habitualmente iteradores, cuyos constructores son * improbables * capaces de lanzar). Pero sí, el hecho de que no ofrezcan una fuerte garantía de excepción no significa que el top/pop no debería. –

+0

'template typename C :: value_type pop (C & c) {typename C :: value_type copy = c.top(); c.pop(); copia de retorno; } 'La única excepción de seguridad es si el dtor del ítem puede tirar, pero arrojar dtors debe ser * extremadamente * raro, y un dtor no lanzador podría fácilmente ser un requerimiento para llamar a este pop(), similar a otros requisitos del contenedor. –

+0

@Roger: Está bien, pero (a excepción de la seguridad) los compiladores no están obligados a implementar RVO, en cuyo caso, la 'copia de retorno 'provocaría que se llamara (una vez más) al constructor de copia. (Los desarrolladores de PD son Very Bad News (tm), porque si ocurre durante el desenrollado de la pila, se invoca 'std :: terminate', o algo por el estilo. Por lo tanto, uno puede/debe asumir que esto no debería suceder). –