2009-02-10 14 views
5

Soy un programador de Java/C# moderadamente experimentado, y recientemente comencé a aprender C++. El problema es que tengo problemas para entender cómo estructurar los diversos archivos de encabezado y código. Esto parece deberse principalmente a mi falta de comprensión sobre cómo el compilador vincula todo junto. Intenté leer algunos libros de texto, pero mis ideas preconcebidas están fuertemente teñidas por mis conocimientos de Java y C#. Por ejemplo, estoy teniendo dificultades para entender el hecho de que los métodos y similares se pueden definir en un espacio de nombres en lugar de solo en una definición de clase.Comprender los compiladores de C++ desde una perspectiva Java/C#

He encontrado muchas guías en C++ -> Java/C#, pero prácticamente nada en el otro sentido. ¿Existen buenos recursos para facilitar la transición Java/C# -> C++, particularmente con respecto a la comprensión del proceso de compilación?

+0

Editar: Gracias a todos los que respondieron, todas las respuestas fueron útiles e informativas. – Whatsit

+0

Para cualquier persona que se encuentre en una situación similar, he encontrado esta página muy útil, en particular la sección A3.3 (Clases): http://www.horstmann.com/ccj2/ccjapp3.html – Whatsit

Respuesta

4

El C++ FAQ es un excelente recurso sobre todas las idiosincrasias de C++, pero es probablemente un poco más avanzado de lo que busca: la mayoría de las preguntas (no solo las respuestas) son misterios incluso para desarrolladores bastante experimentados de C++ .

Creo que si buscas en Google tutoriales en C++, podrás encontrar algo. También es posible que desee intentar aprender el lenguaje ensamblador (o al menos obtener una introducción rápida sobre cómo realmente suceden las cosas en un microprocesador), ya que tanto C como C++ están bastante cerca del hardware en la forma en que hacen las cosas. De ahí viene su velocidad y poder, pero tiene el precio de algunas de las mejores abstracciones que Java ofrece.

Puedo intentar responder a sus preguntas específicas, pero no sé qué tan bien lo haré.

Una de las claves para entender la relación entre los archivos de encabezado y los archivos cpp es comprender la idea de una "unidad de traducción". Un archivo de clase Java puede considerarse una unidad de traducción, ya que es la unidad básica compilada en forma binaria. En C++, casi todos los archivos cpp son unidades de traducción (existen excepciones si haces cosas raras).

Un archivo de encabezado se puede incluir en varias unidades de traducción (y se debe incluir en todas partes que usa lo que se define en el encabezado). La directiva #include literalmente solo hace una sustitución de texto: el contenido del archivo incluido se inserta textualmente donde está la directiva #include. Normalmente desea que su interfaz de clase esté definida en el archivo de encabezado y la implementación en el archivo cpp. Esto se debe a que no desea exponer sus detalles de implementación a otras unidades de traducción que pueden incluir el encabezado. En C++, todo, incluidas las clases, no son realmente objetos ricos, sino fragmentos de memoria a los que el compilador asigna significado ... compilando la misma información de encabezado en cada unidad de traducción, el compilador garantiza que todas las unidades de traducción tengan el la misma comprensión de lo que representa un pedazo de memoria. Debido a la falta de datos abundantes después del tiempo de compilación, cosas como la reflexión son imposibles.El segundo paso en el proceso de compilación de C++ es vincular, que es donde el enlazador toma todas las unidades de traducción compiladas y busca símbolos (generalmente llamadas de función, pero también variables) utilizados en una unidad de traducción pero no definidos allí. Luego busca otra unidad de traducción que defina ese símbolo y "los vincule", de modo que todas las llamadas a una función en particular se dirijan a la unidad de traducción que lo define.

En el caso de los métodos de clase, deben llamarse a través de una instancia de clase, que está detrás de las escenas solo como un puntero a una parte de la memoria. Cuando el compilador ve este tipo de llamadas a métodos, emite un código que llama a una función, pasando implícitamente el puntero, conocido como puntero this, a la función como primer argumento. Puede tener funciones que no pertenecen a las clases (no a los métodos, como dijo, porque un método es propiamente una función miembro de una clase y, por lo tanto, no puede existir sin una clase) porque el vinculador no tiene ningún concepto de una clase. Verá una unidad de traducción que define una función y otra que llama a una función y las une.

Eso terminó siendo mucho más largo de lo que esperaba, y por supuesto es una simplificación excesiva, pero es preciso según mi conocimiento y el nivel de detalle proporcionado ... espero que ayude a algunos. Al menos debería darle un punto de partida para algunos googlear.

4

Esto es algo que me confundió cuando empecé a usar C también. Los libros no hacen un buen trabajo al describir el uso correcto de los encabezados frente a los archivos de códigos.

El compilador funciona cargando cada archivo .cpp y compilándolo independientemente de todos los demás. El primer paso en la compilación es cargar todos los encabezados a los que hace referencia las sentencias #include. Puedes pensar que está haciendo una inserción textual de foo.h donde sea que haya un #include "foo.h".

¿Cuáles son las implicaciones de esto para la estructura de sus archivos? Los archivos de encabezado deben tener las partes del programa necesarias para que se refieran a otros archivos .cpp. Como regla general, las implementaciones no deberían estar en archivos de encabezado. Esto causará problemas. Los archivos de encabezado deben incluir declaraciones de clases, funciones y variables globales (si debe usarlas).

+0

"" Los archivos de encabezado deben incluir declaraciones de clases "" , de hecho, usted * define * clases y * declara * sus funciones miembro en archivos de encabezado. Sin embargo, * definir * esas funciones miembro/miembros estáticos se realiza en archivos fuente . mientras que, clase foo; es una declinación, la clase foo {} es definición. –

1

De hecho, recomendaría evitar las explicaciones sobre los compiladores de C++ y buscar explicaciones en los compiladores de C. En mi experiencia, estos se explican mejor y evitan confundirte con problemas de OOP. Busque material sobre la compilación C por separado. Te habría recomendado un gran folleto de diapositivas de mi alma mater, pero no está en inglés.

La principal diferencia entre la compilación C y Java/C# es que la compilación no crea una entidad resuelta. En otras palabras, cuando compila en Java, el compilador busca archivos de clase ya compilados para cualquier clase referenciada, y se asegura de que todo esté disponible y sea coherente. La suposición subyacente es que cuando finalmente ejecute el programa, esos archivos también estarán disponibles.

Un archivo C compilado, por otro lado, es solo una "promesa". Se basa en una declaración de cómo se verían las dependencias (en forma de declaraciones de funciones), pero no hay garantías de que estén definidas en ningún lugar. El cambio de mentalidad más difícil que debe hacer es pensar en un archivo C no solo como ese archivo, sino más bien como la agregación de ese archivo con todo lo que incluye (es decir, lo que genera el preprocesador). En otras palabras, el compilador no ve archivos de encabezado, parece un archivo grande. El compilador realiza un seguimiento en el archivo de objeto generado de todo lo que "aún falta". Más adelante, en el momento del enlace, el enlazador soluciona esto tratando de llenar todos los espacios en blanco con materiales de los diferentes archivos objeto.

1

es posible que desee saber por qué la compilación y los enlaces también están separados (ya que no veo publicaciones que lo expliquen, y es causa de mucha confusión sin conocer las razones subyacentes de las cosas).

La vinculación y compilación se completa por separado porque (y puede haber más de una razón) de la necesidad de realizar llamadas a la biblioteca. si definió o algo de su estilo, el código que implementa los prototipos de funciones en esos encabezados es parte de la biblioteca que ya está compilada y se encuentra como código objeto en alguna parte. si en su lugar se utilizara un proceso de compilación gigante, necesitaría tener la fuente para esas llamadas a la biblioteca, así como más tiempo durante la compilación porque también estaría compilando el código de la biblioteca.

Cuestiones relacionadas