2010-11-28 9 views
5

No estoy seguro de cómo ordenar mis funciones en C++. En C, simplemente coloqué una función que usa otra función debajo de esa función, tan cerca como sea posible, eso es bastante común. De esta manera:¿Cómo pedir funciones en C++?

void bar() 
{ 
} 

void foo() 
{ 
    bar(); 
} 

Sin embargo, en C++, hay varios tipos de funciones:

  • funciones libres
  • funciones miembro privadas
  • funciones miembro públicas
  • funciones miembro estáticas

Actualmente estoy maki ng mi pedido función depende de la forma en que se ordenan en el archivo .hpp, por ejemplo:

class Foo_bar { 
public: 
    Foo_bar(); 
    void foo(); 
private: 
    int some_member; 
    void bar(); 

Pero ahora, si el constructor utiliza foo() o la barra(), estos serán a continuación el constructor en el archivo fuente, inconsistente con mi orden habitual. Por supuesto, pude reordenar mi encabezado para tener en cuenta eso:

class Foo_bar { 
private: 
    int some_member; 
    void bar(); 

public: 
    void foo(); 
    Foo_bar(); 

Pero creo que es un desastre.

Por otra parte, en Java, lo contrario a mi primer ejemplo parece ser común:

void foo() 
{ 
    bar(); 
} 

void bar() 
{ 
} 

Eso es debido probablemente a la de arriba hacia abajo pensando común en programación orientada a objetos, en contraste con la de abajo hacia arriba pensando común en programación procedural/funcional. Sin embargo, con funciones gratuitas que no tienen prototipos, este estilo descendente no es posible.

¿Es posible ordenar funciones en C++ de forma constante?

+0

Prefiero pedir mis funciones por lo que estoy trabajando - de modo que cuando al abrir un archivo, es probable que lo que quiero ver esté en la parte superior. Dicho esto, tengo mucha práctica en la búsqueda de ":: methodName", así que tal vez mi método no es el mejor;) – sje397

+0

No entiendo la pregunta. Todas las funciones tienen prototipos y puede declarar funciones gratuitas en un archivo de encabezado o en cualquier otro lugar si es necesario. Siempre que tenga declaraciones visibles para cualquier función que use, puede ordenar definiciones de funciones en el orden que elija. ¿Qué le impide ordenar sus funciones de cualquier manera que considere consistente? –

+0

@Charles: vea los comentarios de forneo a continuación. Al parecer, él piensa que usar las características del lenguaje para lo que estaban destinadas es malo ...? Parece que básicamente está haciendo la pregunta altamente subjetiva "No quiero usar las funciones de idioma disponibles para mí, ¿qué sugieres que haga en su lugar?" Es como decir "Realmente no me gustan las llaves, así que trato de ajustar 1 enunciado por bloque para evitarlas, ¿hay alguna forma consistente de hacerlo así que nunca tengo que usar llaves?" y esperando que alguien diga "Sí, si (exp) declaración; if (sameExp) statement2; ¡obtiene 2 estadísticas sin llaves!" Mismo tipo de Q. – Loduwijk

Respuesta

6

Es posible. Tienes que usar forward declaration.

Declare una función antes de definirla, y otras funciones la verán sin problemas incluso si se definieron anteriormente.

lo tanto, debe ser capaz de hacer esto en C++:

void bar(); // forward declaration; note that function bar isn't defined yet 

void foo() 
{ 
    bar(); // foo knows that bar is declared, so it will search for bar's definition 
} 

void bar() // here bar is defined, so foo will use this when needed 
{ 
} 
+0

Es técnicamente posible, sí, pero IMO es bastante feo para declarar todas las funciones gratuitas. No voy a poner prototipos de funciones gratuitas utilizadas únicamente dentro de mi código en un encabezado, confío en Scott Meyers sobre eso. http://www.drdobbs.com/184401197 – forneo

+2

@forneo: Si quieres decir que no lo estás poniendo en un archivo de encabezado, está bien, Darío no dijo que debieras. Puedes declararlo con un prototipo en la parte superior del mismo archivo en el que los estás usando. Si por encabezado te refieres al encabezado (arriba) del mismo archivo, ¿por qué no? Yo prototipo de casi todo en la parte superior de mis archivos, incluso si no es necesario, y está bien; no es horrible, es bueno poder ver una lista de las funciones en la parte superior. Si dices que no harás eso, entonces esa es tu pérdida, estás diciendo "no quiero usar las características del idioma" y no tienes preguntas aquí. – Loduwijk

+0

Supongo que entendí mal, ni siquiera consideré poner los forwards en el mismo archivo. Eso me permitiría hacer pedidos de arriba hacia abajo. – forneo

1

la clase se declara en un archivo de cabecera, ¿verdad? ¿Y ejecutar la mayor parte en un archivo separado? Si simplemente implementa el constructor en el archivo de implementación y no en el encabezado, no creo que experimente el problema que mencionó (porque el encabezado completo se verá antes de que se vea al constructor llamar al foo() o al bar().

+0

Sí, no hay ningún problema técnico, solo digo que es inconsistente con mi ordenamiento ascendente habitual. – forneo

1

El pedido de funciones gratuitas en C++ obedece a las mismas reglas que usted mencionó, pero como dijo darioo, puede reenviarlas y ordenar las definiciones de funciones de la forma que desee. Esa es también la forma preferida: declarar todo en el encabezado y poner TODO definiciones en el archivo de origen. Sin embargo, esto no es posible para las plantillas, sin algunas soluciones temporales anti-plantilla no triviales y no generales.

En una clase, las cosas suelen ser diferentes, porque casi no hay casos wh Antes de implementar por completo su clase en un encabezado, las declaraciones siempre se leen cuando define las funciones en el archivo fuente.

Normalmente ordeno funciones en "funcion", y grupo ej. getters y setters, constructor/destructor (si es posible).

+0

Ver mi respuesta a la respuesta de darioo, no creo que todas las funciones tengan que declararse en el encabezado, Scott Meyers hace algunos puntos bastante buenos. – forneo

+0

De acuerdo, no todas las funciones internas específicas del archivo fuente deben estar en el encabezado, si no lo desea allí. Pero luego * puede * reenviar declarar las funciones en el archivo de origen, y ordenarlas debajo de estas declaraciones de la forma que mejor le parezca. – rubenvb

+0

De acuerdo, aunque incluso con plantillas, me gusta hacer que las cosas con plantilla real sean triviales con el código subyacente que no es de plantilla. Es un poco más de trabajo, pero puede evitar la hinchazón y ayuda a mantener los encabezados relativamente legibles. Aunque en el tema de la hinchazón, es posible que las plantillas sean menos propensas a la hinchazón de lo que alguna vez fueron. – Steve314

0

Personalmente, me gusta ver las cosas a las que se hará referencia desde otro lugar (que las personas necesitarán encontrar/leer a menudo) cerca de la parte superior del archivo. Los internos que, una vez estables, pueden ser olvidados quedan para más adelante.

Sin embargo, hay inconsistencias. Por ejemplo, en una clase, implica poner lo público primero, lo interno más tarde. Pero la visibilidad predeterminada para una clase (lo que obtienes naturalmente) es privada, y (particularmente si tengo métodos de estilo en línea) generalmente pongo cualquier información privada en el frente. Incluso puede ser un error que un método de estilo en línea haga referencia a una variable miembro antes de que se haya definido. Lo siento, tengo un problema temporal de memoria.

Pero básicamente, lo principal es unir cosas que son similares o lógicamente relacionadas. Un método de inicio será adyacente a un método final, un método Step_To_Next adyacente a un método Step_To_Prev, etc. Agrupar con propósitos similares, parámetros similares y, comúnmente, ser usados ​​juntos, todos son buenos.

Lo que se llama principalmente es un detalle de implementación, por lo que no es algo que deba destacarse necesariamente en los archivos de encabezado que leerá el usuario de la biblioteca, aunque en el código de implementación las cosas pueden ser diferentes. Como otros han señalado, las declaraciones anticipadas permiten cierta libertad con esto.

Lo más importante (1) adopte un estilo uniforme y (2) no se preocupe demasiado por los casos ambiguos.

+0

En el comentario de si puede o no puede usar un atributo de miembro aún no declarado de un método de miembro con línea, la respuesta es que puede, es legal y común.'struct test {void foo() {std :: cout << x << std :: endl; } int x; }; 'es el código correcto. –

1

Su preocupación por el reordenamiento de las funciones en la definición de la clase no es correcta, como lo aclaran las dos citas siguientes del Estándar C++ 03.

$ 9,2/2- "Una clase se considera un definido completamente de tipo de objeto (3.9) (o completa tipo) en el cierre} de la clase-especificador. dentro del miembro-especificación de la clase , la clase se considerarse completo dentro de la función cuerpos, los argumentos por defecto y constructor ctor-inicializadores (incluyendo tales cosas en anidados clases). de lo contrario, es considerado como incompleta dentro de su propio miembro-especificación de la clase ."

Y

$ 3.4.1/8 - "Un nombre usado en la definición de una función miembro (9.3) de clase X siguiente declaratorid29 de la función) se declarará en uno de las siguientes maneras:

- antes de su uso en el bloque en el que se utiliza o en un bloque que lo contiene (6.3), o

- será un miembro de la clase X o ser un miembro de de una clase base de X (10.2), o

- si X es una clase anidada de la clase Y (9.7), será un miembro de Y, o será un miembro de una clase base de Y (Esta búsqueda se aplica a su vez a clases de cerramiento de Y, a partir de la clase envolvente más interna ), 30) o

- si X es una clase local (9.8) o es una clase anidada de una clase local, antes de la definición de clase X en un bloque que encierra la definición de clase X, o

- si X es un miembro del espacio de nombres N o es una clase anidada de una clase que es un miembro de N, o es un lo clase de cal o una clase anidada dentro de una clase local de una función que es miembro de N, antes de la definición de función miembro, en el espacio de nombres N o en uno de los espacios de nombres de N que incluyen.

Como regla general, en C++, las definiciones de funciones deben estar visibles en el momento de su uso. La única excepción es el caso de las funciones de los miembros de la clase, como se ilustra en las citas anteriores.

Por lo tanto, esto significa que las funciones miembro de la clase a las que debe llamar el constructor no necesitan definirse antes que el constructor léxicamente.

2

Realmente es una buena pregunta, porque la legibilidad tiene un gran impacto en quien lea el código después de ti.

Hay 3 tipos de personas que van a leer el código de una clase:

  • aquellos que desean lo usa (y no me importa mucho acerca de su funcionamiento interno)
  • aquellos que desean heredar de su clase (y no les importa mucho acerca de su funcionamiento interno)
  • aquellos que deseen introducirse en su clase, y por lo tanto muy cuidado de sus partes internas

por esta razón, trato de ordenar los encabezados de modo que cualquiera de nosotros ER puede parar una vez que consiguió lo que estaba buscando, lo que significa:

class Foo 
{ 
public: 
    // types 
    // static methods 
    // methods (usually constructors, 
    //   then simple accessors, 
    //   then more complicated stuff) 

protected: 
    // same pattern 

private: 
    // same pattern 
    // attributes 
}; 

// Free functions about this class 

// Implementation of inline/template methods 

veces necesito para declarar algunos tipos de antemano a pesar de que son privadas, pero esto es raro. El objetivo de este orden es minimizar absolutamente la cantidad de cosas que un lector tiene que leer antes de obtener lo que quiere (y deja de leer y vuelve a lo que estaba haciendo antes de tener que interrumpirse para ver su código).

Entonces, en cuanto a los métodos de "ayudantes", que depende del tipo de código:

  • de código de la plantilla, utilizo un espacio de nombres "detalles", que es a la vez claro para el lector que no debe estar preocupado al respecto y aislar los nombres en su propio espacio de nombres para que no aparezcan en las herramientas de finalización de código
  • para el código regular, utilizo un espacio de nombre anónimo dentro del archivo fuente, que es aún mejor ya que en realidad genera símbolos invisibles y no corro el riesgo de violar ODR.

Si algún código puede requerir una gran cantidad de ayudantes, que tienden a crear un archivo de cabecera dedicado en el directorio de origen, dando la siguiente estructura:

include/ 
    foo.hpp 

src/ 
    fooImpl.hpp --> #include "foo.hpp" 
    foo.cpp  --> #include "fooImpl.hpp" 

con el fin de proporcionar una lista de declaraciones de el lector, porque es más fácil navegar por una lista de declaraciones que extraer las declaraciones de una lista de definiciones, independientemente de la sangría y el estilo.

Y, por supuesto, siempre que sea más fácil, yo siempre pedir la lista de declaraciones y la lista de las definiciones por igual ...

+0

También hay personas que desean aprender de su código. –

+0

@druciferre: cuando llegue al punto donde la gente busca aprender de mi código, estaré muy feliz: D –