parametros - librerias de c++
Estilo de programaciĆ³n C++ (16)
El estilo que programe depende de usted. Solo asegúrate de entender el estilo "feo" que otros usan.
Soy un programador de Java viejo (pero no demasiado viejo) que decidió aprender C ++. Pero he visto mucho del estilo de programación C ++, es ... bueno, ¡malditamente feo!
Todo eso de poner la definición de clase en un archivo de cabecera, y los métodos en un archivo fuente diferente: llamadas de la nada de la nada, en lugar de usar métodos dentro de las clases . Todo lo que parece ... mal!
Entonces, finalmente, ¿hay alguna razón para que continúe con esta masacre en el OOP, y cualquier cosa que sea buena y justa en programación, o puedo simplemente ignorar las anticuadas convenciones de C ++, y usar mi buen estilo de programación Java?
Por cierto estoy aprendiendo C ++, porque quiero hacer programación de juegos.
Aquí hay un ejemplo:
En un sitio web de C ++ encontré una implementación de Windows:
class WinClass
{
public:
WinClass (WNDPROC wndProc, char const * className, HINSTANCE hInst);
void Register ()
{
::RegisterClass (&_class);
}
private:
WNDCLASS _class;
};
Esa clase se encuentra en un archivo de cabecera y el constructor:
WinClass::WinClass (WNDPROC wndProc, char const * className, HINSTANCE hInst)
{
_class.style = 0;
_class.lpfnWndProc = wndProc; // Window Procedure: mandatory
_class.cbClsExtra = 0;
_class.cbWndExtra = 0;
_class.hInstance = hInst; // Owner of the class: mandatory
_class.hIcon = 0;
_class.hCursor = ::LoadCursor (0, IDC_ARROW); // Optional
_class.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // Optional
_class.lpszMenuName = 0;
_class.lpszClassName = className; // Mandatory
}
Está ubicado en un archivo fuente .cpp.
Lo que podría hacer es:
class WinClass
{
public:
WinClass (WNDPROC wndProc, char const * className, HINSTANCE hInst)
{
_class.style = 0;
_class.lpfnWndProc = wndProc; // Window Procedure: mandatory
_class.cbClsExtra = 0;
_class.cbWndExtra = 0;
_class.hInstance = hInst; // Owner of the class: mandatory
_class.hIcon = 0;
_class.hCursor = ::LoadCursor (0, IDC_ARROW); // Optional
_class.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // Optional
_class.lpszMenuName = 0;
_class.lpszClassName = className; // Mandatory
}
void Register ()
{
::RegisterClass (&_class);
}
private:
WNDCLASS _class;
};
Y ahora el constructor está dentro de su clase.
Encuentre un estilo que funcione para usted, al igual que todos los demás. Nadie te obliga a usar uno de los estilos "feos", a menos que sea tu empleador quien aplique un documento de pautas. ;-)
Sin embargo, tenga en cuenta que colocar definiciones de funciones miembro dentro de la clase en oposición al exterior tiene una semántica diferente. Cuando define una función de miembro dentro de la clase, está implícitamente en línea, para bien o para mal.
Los archivos .h y .cpp a menudo separan las declaraciones de las definiciones, y aquí hay algún tipo de orden y encapsulación conceptual. los encabezados tienen el "qué", los archivos de implementación tienen el "cómo".
Encuentro que tener todo en un archivo en Java es desorganizado. Entonces, cada uno para ellos.
Las convenciones y expresiones idiomáticas de C ++ tienen un razonamiento bien pensado, al igual que cualquier otro idioma para los modismos que adopta su comunidad.
¡Creo que es mejor java (como verbo) cuando escribes Java y C ++ cuando tienes C ++! Apreciará por qué con la experiencia del idioma. Lo mismo que cambiar a cualquier idioma nuevo.
Me preocupa por el tono de su pregunta que puede estar leyendo un código C ++ malo en el aprendizaje de C ++. El código bien escrito generalmente no es feo en ningún idioma. Como punto de partida, puede probar las Preguntas frecuentes en línea de C ++ , especialmente el capítulo sobre el aprendizaje de C ++.
Si quieres hacer programación de juegos, probablemente quieras trabajar con otros desarrolladores de C ++, y esto significa que debes hacer las cosas de una manera que ellos puedan entender. Si espera tener alguna colaboración, su código tendrá que estar en un estilo razonable de C ++. Si tiene la intención de ser un desarrollador solitario, y su código esencialmente muere con usted, use el estilo que desee.
Además, C ++ y Java son dos idiomas diferentes y deben usarse de diferentes maneras. Hay razones para los archivos de encabezado (piénselo como declarar la interfaz), por ejemplo.
Llamar a funciones de la nada, en lugar de usar métodos dentro de las clases ; Todo lo que parece ... mal!
Entonces, finalmente, ¿hay alguna razón para que continúe con esta masacre a la OOP?
Bueno, llamar funciones que no pertenecen a clases no es OOP, es programación de procedimientos. Por lo tanto, creo que realmente está teniendo dificultades para salir de la mentalidad de OOP. (Ciertamente, C ++ tiene muchas enfermedades, pero la programación de procedimientos no es una de ellas).
C ++ es un lenguaje multi-paradigma, no solo un lenguaje OO. Las plantillas son una forma de programación genérica que se puede aplicar a los paradigmas de procedimiento, programación orientada a objetos y metaprogramación de C ++. Con el próximo estándar de C ++, verá algunos paradigmas funcionales agregados también.
Todo eso de poner la definición de clase en un archivo de cabecera, y los métodos en un archivo fuente diferente;
Esto tiene su origen en el lenguaje de programación C, allá por los años 70. C ++ fue diseñado para ser compatible con versiones anteriores de C.
El lenguaje de programación D es un intento de corregir muchos de los males de C ++ (y como dije antes, hay muchos), pero mantiene las buenas características de C ++, incluidos todos los diversos paradigmas que admite C ++: procedural, OOP, metaprogramación y funcional.
Si quieres salir de la mentalidad de OOP, ¡prueba D! Luego, cuando tenga la sensación de cómo mezclar y combinar los diferentes paradigmas, puede (si lo desea) volver a C ++ y aprender a manejar su sintaxis y otros males.
PD: uso C ++ a diario y soy un fanático de él; es mi lenguaje de programación favorito. Pero como sabe cualquier programador experto en C ++, C ++ sí tiene sus inconvenientes. Pero una vez que domines los diferentes paradigmas y sepas cómo evitar las enfermedades de C ++, tendrás a tu disposición una herramienta increíblemente poderosa (para eso es todo un lenguaje).
¡Buena suerte en tus aventuras!
Cuando el código se genera, el preprocesador de C ++ crea una unidad de traducción. Comienza con un archivo .cpp, analiza los #includes que captan el texto de los encabezados y genera un gran archivo de texto grande con todos los encabezados y el código .cpp. Luego, esta unidad de traducción se compilará en un código que se ejecutará en la plataforma a la que se dirige. Cada unidad de traducción termina como un archivo de objeto.
Por lo tanto, los archivos .h se incluyen en varias unidades de traducción, y un archivo .cpp se incluye en uno.
Si los archivos .h contienen muchas cosas (incluida la implementación), las unidades de traducción serían correspondientemente más grandes. Los tiempos de compilación aumentarían, y cuando cambias algo en un encabezado ... cada unidad de traducción que la usa debería ser recompilada.
Entonces ... minimizar las cosas en archivos .h mejora drásticamente el tiempo de compilación. Puede editar un archivo .cpp para cambiar una función, y solo esa unidad de traducción necesita ser reconstruida.
Para completar la historia sobre la compilación ...
Una vez que todas las unidades de traducción se compilan en archivos objeto (código binario nativo para su plataforma). El enlazador hace su trabajo y los une en su archivo .exe, .dll o .lib. Un archivo .lib se puede vincular a otra compilación para que pueda ser reutilizado en más de un .exe o .dll.
Supongo que el modelo de compilación de Java es más avanzado, y las implicaciones de dónde pones tu código tienen menos significado.
Eso se llama interfaz de separación e implementación. Eso permite a los clientes descubrir cómo llamar a su clase sin tener que pasar por todo el código también. Francamente, creo que es prácticamente una locura que la gente lo considere "incorrecto".
Sin embargo, le complacerá saber que muchos desarrolladores de C ++ están de acuerdo con usted en ese aspecto. Así que adelante y ponga toda su implementación en archivos * .h. Hay una escuela de estilo que está de acuerdo contigo.
Si desea implementar todos sus métodos dentro de las definiciones de clase, tendrá grandes archivos de encabezado de monstruo (no es una buena idea declarar clases en archivos cpp) y posiblemente solo un archivo cpp (el que tenga su función principal).
Entonces es una cuestión de conveniencia.
Editar:
Encuentro la separación declaración-implementación realmente útil. Cuando tengo clases de monstruo con ~ 150 métodos cuya implementación se genera usando muchos trucos de preprocesador, a menudo quiero poder decir la diferencia entre ...
- qué puede hacer la clase (su interfaz, dada por su declaración)
- cómo lo hace la clase (su implementación)
No poder hacer eso en C # o Java es bastante frustrante, especialmente cuando no tengo Intellisense a la mano.
Si está escribiendo una DLL de C ++ y coloca algún código en los encabezados, terminará con un desastre difícil de depurar. La DLL contendrá el código de los archivos .cpp, pero los usuarios de la DLL tendrán algunos de los códigos insertados en ellos mismos.
Esto es realmente malo en Windows donde te encuentras con cosas como diferentes asignadores de montón en diferentes partes del programa y si cambias la implementación, los llamadores de tu DLL utilizarán el código anterior de los encabezados incluidos.
Al igual que un programador estereotípico que programa en un paradigma específico, usted decidió que ciertas cosas que no le son familiares son feas.
C ++ es un lenguaje diferente, es multiparadigma y tiene muchos remanentes de C. Como otros han dicho, así son las cosas.
Los archivos de encabezado están ahí para que el compilador compruebe la sintaxis básica sin conocer la implementación. Si usted es un programador de Java pesado, debe estar bastante familiarizado con todo el programa a un concepto de interfaz. Piense en el archivo de encabezado donde está la interfaz.
Si no me crees, ve algunos Mods para juegos. Es muy posible encontrar el archivo de encabezado de CryEngine2 en alguna parte porque las modificaciones de Crysis necesitarían hablar con él, pero no es necesario que comprenda cómo funciona. Los archivos de encabezado definirán cómo, en última instancia, una función de llamada debe configurar la pila para llamar a otra función.
No ser condense, pero creo que lo que realmente necesitas es programar en algo completamente diferente. Investigue otros paradigmas y encuentre algunos idiomas, y comience a escribir algunas aplicaciones de juguetes. Al principio, las cosas se verían feas, pero eventualmente verás las ventajas.
Dije lo mismo sobre la programación funcional también. Ahora me quejo de cómo PHP eliminó la programación funcional con arreglos.
Creo que muchos programadores cortaron sus dientes con productos MicroSoft (y su código de ejemplo) y / o programación para Windows API y las primeras convenciones de codificación de Microsoft que se usaron en el fragmento de código en su pregunta (es decir, notación húngara, uso de mayúsculas de tipos definidos, etc. ..). Detesto mirar el código fuente de MicroSoft que parece que se ha ejecutado a través de una trituradora de papel de corte transversal y pegado nuevamente. Pero una fea convención de codificación no es una función o reflejo del lenguaje C ++ que no encuentro más bello o feo que la mayoría de los otros lenguajes.
De hecho, encuentro que la sintaxis de C ++ con palabras clave mínimas, llaves y un rico conjunto de operadores simbólicos sirve para no distraer o excluir lo importante: mis variables, mi tipo de definición, mis métodos; que, por supuesto, me permite hacer el código más hermoso de todos :-)
La declaración y definición separadas es la menor de las diferencias entre C ++ y Java. La principal diferencia en C ++ moderno es la importancia de la "semántica de valores".
Debido a la falta de recolección de basura, pero el excelente soporte para tipos de construcción que se comportan como valores autónomos, un buen estilo de C ++ implica escribir tipos de valores de buen comportamiento, con una construcción consistente, copia, construcción, asignación, intercambio y destrucción.
El intercambio en particular es muy importante de recordar porque no está respaldado directamente por el lenguaje, pero realmente debería estar allí en cualquier tipo de valor.
Observe cómo funciona la biblioteca estándar de C ++ y cómo espera que se comporten sus tipos.
Intente apuntar (no siempre es posible) para una ausencia total de punteros descubiertos o usos del nuevo operador. Oculte esos detalles dentro de las clases para garantizar que se usan correctamente, y los completa en una semántica de valores.
Eso no es feo en absoluto. Yo diría que es ... mhhh diferente. Llevar tu viejo estilo a una nueva plataforma, eso es feo (he tenido muchas discusiones sobre esto con el Sr. Skeet).
Recuerde que Java se definió años después de C ++, por lo que es razonable que hayan corregido algunas construcciones (de la misma manera C # aprenda de Java).
Entonces, sugeriría mantener ese estilo de C ++.
Piensa sobre esto. Los archivos de encabezado son como declaraciones de interfaz y los archivos cpp son implementación.
Creo que C ++ no tiene la palabra clave "interfaz", y esta es la forma en que separa la interfaz de una clase de su implementación. Similar a esto en Java:
public interface Some {
public void method();
}
public class SomeImpl implements Some {
public void method(){}
}
Además de lo que otros han dicho aquí, hay problemas aún más importantes:
1) Las unidades de traducción grandes conducen a tiempos de compilación más largos y tamaños de archivo de objeto más grandes.
2) Dependencias circulares! Y este es el grande. Y casi siempre se puede arreglar dividiendo los encabezados y la fuente:
// Vehicle.h
class Wheel {
private:
Car& m_parent;
public:
Wheel( Car& p ) : m_parent( p ) {
std::cout << "Car has " << m_parent.numWheels() << " wheels." << std::endl;
}
};
class Car {
private:
std::vector< Wheel > m_wheels;
public:
Car() {
for( int i=0; i<4; ++i )
m_wheels.push_back( Wheel( *this ) );
}
int numWheels() {
return m_wheels.size();
}
}
Independientemente del orden en que los coloque, siempre habrá una falta de definición del otro, incluso si se usan las declaraciones hacia delante, no funcionará, ya que en la función los cuerpos están usando detalles sobre el símbolo de cada clase.
Pero si los divides en archivos .h y .cpp correctos y usas declaraciones avanzadas, satisfarán al compilador:
//Wheel.h
//-------
class Car;
class Wheel {
private:
Car& m_parent;
public:
Wheel( Car& p );
};
//Wheel.cpp
//---------
#include "Wheel.h"
#include "Car.h"
Wheel::Wheel( Car& p ) : m_parent( p ) {
std::cout << "Car has " << m_parent.numWheels() << " wheels." << std::endl;
}
//Car.h
//-----
class Wheel;
class Car {
private:
std::vector< Wheel > m_wheels;
public:
Car();
int numWheels();
}
//Car.cpp
//-------
#include "Car.h"
#include "Wheel.h"
Car::Car() {
for( int i=0; i<4; ++i )
m_wheels.push_back( Wheel( *this ) );
}
int Car::numWheels() {
return m_wheels.size();
}
Ahora el código que en realidad tiene que conocer detalles sobre la segunda clase solo puede incluir el archivo de encabezado que no necesita conocer detalles sobre la primera clase.
Los encabezados solo proporcionan las declaraciones mientras que los archivos fuente proporcionan las definiciones . O bien, otra forma de decirlo: los encabezados te dicen qué hay allí ( qué símbolos son válidos para usar ) y la fuente le dice al compilador qué hacen realmente los símbolos. En C ++ no necesita nada más que un símbolo válido para comenzar a usar lo que sea.
Confíe en que C ++ tiene una razón para este modismo, porque si no lo hace, le causará muchos dolores de cabeza en el futuro. Lo sé :/
La mayoría de las respuestas son sobre la separación de encabezados y unidades de compilación. Estoy de acuerdo con la mayoría, debes usarlo porque es más eficiente, más limpio, más amigable para el usuario para tus compañeros de trabajo o simplemente porque ... (y notarás que la ventaja del tiempo de compilación no es demasiado larga a partir de ahora).
De todos modos, solo quería publicar en la otra parte de la pregunta: ¿C ++ es una masacre OOP?
C ++ es un lenguaje multiparadigm. Permite programación procedural , orientada a objetos y genérica . Y eso es una virtud, no un defecto. Todavía puede usar POO puro si lo desea, pero seguramente su código se beneficiará al aprender y saber cuándo usar otros paradigmas.
Como ejemplo, no me gustan las clases de utilidad , donde todas las funciones de miembros son estáticas y no hay datos. No hay razón para crear una clase de utilidad más que simplemente agrupar un conjunto de funciones libres bajo un nombre común. Debes hacerlo en Java, ya que insiste en la sintaxis OO pura que, como comentó Tom , no es lo mismo que OO real. En C ++, la definición de un espacio de nombres y un conjunto de funciones gratuitas ofrece una solución similar sin necesidad de bloquear la creación de objetos al declarar un constructor privado o permitir a los usuarios crear instancias de objetos sin sentido a partir de una clase vacía.
Para mí, la experiencia (una vez fui programador solo de Java) me ha enseñado que hay diferentes herramientas que se adaptan mejor a diferentes problemas. Todavía tengo que encontrar un martillo dorado y la ventaja de C ++ es que puedes elegir un martillo diferente para cada tarea diferente, incluso en la misma unidad de compilación.
Corrección : litb me informa en un comentario que WNDCLASS es una estructura en la API win32. De modo que la crítica de la clase que no usa listas de inicialización es simple tontería y, como tal, la estoy eliminando.