2009-06-25 15 views
5

tengo una clase con dos funciones miembro que comparten un trozo de código:¿Cuál es mejor para una pieza de código que se repite?

void A::First() 
{ 
    firstFunctionEpilogue(); 
    sharedPart(); 
} 

void A::Second() 
{ 
    secondFunctionEpilogue(); 
    sharedPart(); 
} 

Actualmente firstFunctionEpilogue(), secondFunctionEpilogue() y sharedPart() no son llamadas de función, pero sólo piezas de código, siendo duplicada sharedPart() código. Quiero deshacerme de la duplicación.

La pieza compartida de código no necesita acceso a ningún miembro de la clase. Así que se puede aplicar como cualquiera de los tres:

  • una función miembro estática,
  • una función miembro no estática const o
  • una función local.

¿Qué variante es mejor y por qué?

+0

El hecho de que esta parte del código aparezca dos veces, no significa que tenga que hacerlo solo. ¿Qué es lo que quieres ganar? –

+1

¿Qué haces en sharedPart() si no depende ni altera el estado de un objeto? – Tobias

+6

@ammoQ: ¿Sugiere duplicar el código en ambos lugares? Esa es una idea horrible –

Respuesta

5

Si su función accede al estado pero no lo cambia, utilice una función de miembro de la función.

su caso:

Si la función 1) no necesita tener acceso a cualquier miembro del código, y 2) se relaciona con esa clase, a continuación, hacer que sea una función estática de la clase.

De esta forma está claro que no está modificando el estado, ni se basa en el estado del objeto.

Un caso adicional que no ha mencionado:

Hay otra cosa que puede hacer también. Y eso es para que su SharedPart tome un puntero de función miembro y para llamarlo y luego procesar su cuerpo principal. Si tiene muchas First(), Second(), Third(), Fourth(), ... tales funciones, esto puede conducir a una menor duplicación de código. De esta forma, no es necesario que siga llamando a SharedPart(); al final de cada función miembro, y puede volver a utilizar First(), Second(), Third(), ... sin llamar al SharedPart() del código.

+1

Prefiero hacer que sea una función no miembro (quizás escondida en un espacio de nombres secundario para dejar en claro que no se debe usar normalmente), pero un miembro estático también funcionaría. Lo principal es que si no necesita acceso a miembros privados de la clase, no debe * tener * acceso. – jalf

+0

"si no necesita acceso a miembros privados de la clase, no debe * tener * acceso". Personalmente me interesaría más por esto si no fuera por el hecho de que casi todas las funciones de los miembros ya tienen acceso a miembros privados a los que no necesitan acceso, específicamente porque para usar un miembro necesitan acceso al lote completo. De vez en cuando puedes retroceder para no arruinarte ;-) –

+0

Por lo que veo, mucha gente ha leído C++ efectivo aquí :) –

1

O podría ser de una clase diferente.

O, si es un miembro, podría ser virtual.

Hay muchas decisiones, y no me preocuparía demasiado por eso. En general, opto por una función de miembro const non-static como valor predeterminado a menos que tenga una buena razón para no hacerlo de esa manera.

  1. Prefiero estática si los clientes necesitan llamar sin tener una instancia
  2. Prefiero funciones locales si no quieren el desorden en el archivo .h o si desea que completamente oculto en el .c
+0

+1 para funciones locales ... no sé por qué estás desvalorizado – Tom

+0

La actitud hacia las funciones locales es un relacionado con la pureza Las funciones locales rompen muchas (bueno, todas) de los principios de diseño, pero a veces pueden ser prácticas.Dado que esta pregunta está etiquetada como "mejores prácticas", "goto" y "funciones locales" son probablemente respuestas incorrectas. – ima

+0

Di una razón para preferirlas. Si necesita ocultar algo de la .h, úselas, de lo contrario, probablemente no los uses. –

3

yo diría:

  • probablemente no importa, por lo que no es tanto la "mejor práctica" como "simplemente no hacer nada loco".
  • Si la clase y todos sus miembros están definidos en su encabezado, entonces una función de miembro estático privado es probablemente la mejor, ya que indica claramente "no para clientes". Pero hay formas de hacer esto para una función que no es miembro: no la documente, ponga un comentario "no para clientes", y pegue todo en namespace beware_of_the_leopard.
  • Si las funciones miembro de la clase están definidas en un archivo .cpp, las funciones pequeñas de ayuda como esta son mejores como funciones gratuitas en el archivo .cpp. Ya sea estático, o en un espacio de nombre anónimo.
+0

Esa es prácticamente la respuesta que iba a dar. Si no necesita ningún acceso a los miembros de la clase y solo los métodos de clase lo llaman, conviértalo en una función en un espacio de nombre anónimo en el archivo de clase .cpp. –

0

una función miembro estática, una función miembro const no estática o una función local .

En general, debería ser una función miembro de otra clase, o al menos miembro no estático de la propia clase. Si esta función solo se llama desde los miembros de la instancia de una clase, probablemente su significado lógico requiera una instancia, incluso si la sintaxis no. ¿Puede algo más que este objeto proporcionar parámetros significativos o hacer uso del resultado?

A menos que tenga sentido llamar a esta función desde fuera de la instancia del objeto, no debe ser estática. A menos que tenga sentido llamar a esta función sin acceder a su clase en absoluto, no debe ser local.

Ejemplos de préstamos del comentario de Brian: si esta función cambia el estado global, debe ser miembro de una clase de estado global; si esta función escribe en el archivo, debe ser miembro de una clase de formato de archivo; si es una pantalla refrescante, debe ser miembro de ... etc Incluso si se trata de una expresión aritmética simple, puede ser útil convertirla en miembro (estático o no) de alguna clase ArithmeticsForSpecificPurpose.

1

Que sea un no-miembro de la función

La pieza de código compartido no necesita acceso a cualquier miembro de la clase.

Como regla general, si un fragmento de código no necesita acceso a ningún miembro de la clase, ¡no lo haga funcionar como miembro! Intenta encapsular tus clases tanto como sea posible.

Sugeriría hacer una función que no sea miembro en un espacio de nombre separado que llamaría a los métodos públicos y luego llamar a la función que hizo para el código compartido.

Aquí es un ejemplo de lo que quiero decir:

namepsace Astuff{ 
    class A{...}; 

    void sharedPart(){...}; 

    void first(const A& a); 
    void second(const A& a); 
} 

void Astuff::first(const A& a){ 
    a.first(); 
    sharedPart(); 
} 
0

Que sea una función no-amigo no miembro. Scott Meyer's tiene una gran explicación para este here (y también el Item 23 de Effective C++ 3rd Edition).

0

Como regla general, intente mantenerlo lo más local posible pero tan visible como sea necesario.

Si todo el código que llama a la función reside en el mismo archivo de implementación, esto significa mantenerlo localmente en el archivo de implementación.

Si lo convierte en un método privado estático de su clase, las implementaciones, incluida su clase, no podrían llamarlo, pero aún sería visible para ellas.Por lo tanto, cada vez que cambie la semántica de ese método, todas las implementaciones, incluidas sus llamadas, tendrán que volver a compilarse, lo que es una carga, ya que, desde su punto de vista, ni siquiera necesitan conocer esos seminarios.

Por lo tanto, para minimizar las dependencias innecesarias, querrá convertirla en una función global estática.

Sin embargo, si alguna vez se encuentra repitiendo esta función global en múltiples archivos de implementación, sería el momento de mover la función a un par de archivos de cabecera/implementación separados, para que todos los llamantes puedan incluirla.

Si coloca esa función en un espacio de nombre, en el alcance global, o como una función estática en una clase, realmente está de buen gusto.

En una nota final, si opta por la función estática global, hay una versión "más similar a C++": espacios de nombres anónimos. Tiene la buena propiedad de que puede almacenar estado y también evita que los usuarios puedan incluso declarar cualquiera de sus funciones.

// in your .cpp file 
namespace /*anonymous*/ 
{ 
    void foo() 
    { 
    // your code here 
    } 
}; 

void MyClass::FooUser1() { foo(); } 
void MyClass::FooUser2() { foo(); } 
Cuestiones relacionadas