2010-04-06 14 views
13

Agregar funcionalidad a una clase se puede hacer agregando un método o definiendo una función que toma un objeto como su primer parámetro. La mayoría de los programadores que conozco elegirían para la solución de agregar un método de instancia.¿Elegir entre métodos de instancia y funciones gratuitas?

Sin embargo, a veces prefiero crear una función separada. Por ejemplo, en el código de ejemplo siguiente Area y Diagonal se definen como funciones gratuitas en lugar de métodos. Lo encuentro mejor de esta manera porque creo que estas funciones proporcionan mejoras en lugar de la funcionalidad central.

¿Esto se considera una buena/mala práctica? Si la respuesta es "depende", ¿cuáles son las reglas para decidir entre agregar un método o definir una función separada?

class Rect 
{ 
public: 
    Rect(int x, int y, int w, int h) : 
     mX(x), mY(y), mWidth(w), mHeight(h) 
    { 
    } 

    int x() const { return mX; } 

    int y() const { return mY; } 

    int width() const { return mWidth; } 

    int height() const { return mHeight; } 

private: 
    int mX, mY, mWidth, mHeight; 
}; 


int Area(const Rect & inRect) 
{ 
    return inRect.width() * inRect.height(); 
} 


float Diagonal(const Rect & inRect) 
{ 
    return std::sqrt(std::pow(static_cast<float>(inRect.width()), 2) + std::pow(static_cast<float>(inRect.height()), 2)); 
} 
+0

Una nota al margen, el uso de pow con una potencia constante de dos es extremadamente ineficiente en comparación con solo deletrear x * x + y * y + z * z usted mismo. –

+0

@Mark B: Gracias, no sabía eso. – StackedCrooked

+1

Vea el clásico _ [Cómo las funciones no miembro mejoran la encapsulación] (http://drdobbs.com/cpp/184401197) _. ("Cuando piense en la encapsulación, debería pensar en las funciones que no son miembro"). También vea http://stackoverflow.com/q/1692084/140719. – sbi

Respuesta

14

GoTW #84 exploró esta pregunta con respecto a std::string. Tendía a la idea más bien opuesta: la mayoría de las funciones deberían ser globales a menos que realmente necesiten ser miembros.

Diría que la C++ más moderna tiende a la misma idea. Si mira a través de Boost, por ejemplo, se hace bastante con las funciones gratuitas.

+4

Me encanta esta cita de Scott Meyers contenida en el artículo: > Comenzaré con la frase final: si está escribiendo una función que puede implementarse como miembro o como un no miembro amigo, usted debería preferir implementarlo como una función no miembro. Esa decisión aumenta la encapsulación de clase. Cuando piense en la encapsulación, debería pensar en las funciones que no son miembro. –

+2

Mi único probablemente con esto es la contaminación del espacio de nombres. ¿Es la geometría flotante :: Area (const Geometry :: Rect & r) mejor que Float Geometry :: Rect :: Area()? En realidad, no desea que el Área de funciones libre esté en el espacio de nombres global, por lo que debe colocarlo en un espacio de nombres. Y en ese punto parece una función de clase estática. – jmucchiello

+2

@jmucchiello: Sí, la mayoría de las funciones gratuitas deben estar en espacios de nombres. Sin embargo, no creo que parezca funciones estáticas de miembros: estoy acostumbrado a 'x :: y' que significa" función y en el espacio de nombres x ". –

1

Yo diría que depende de cuáles sean sus objetivos. Si la función es una función miembro, puede usarla. y -> notación con él, y está mejor integrado con la clase. También permite el polimorfismo. Si desea que su función sea virtual, debe ser una función miembro.

Sin embargo, existe la creencia de que es mejor encapsular para hacer que las funciones funcionen como miembros solo si es necesario. Entonces, si la función no necesita acceso a las variables miembro (presumiblemente porque puede hacer lo que tiene que hacer solo con las funciones públicas), entonces la convierte en una función separada que no es miembro de la clase. De esa manera no puede meterse con las partes internas incluso por accidente.

Otra consideración es que si no se trata de una función miembro, debe preocuparse de si otros tipos pueden o no ser convertidos al tipo que ahora es el primer parámetro de la función. A veces eso es deseable y otras no. Si se trata de una función miembro, no hay problema (o una oportunidad, dependiendo de lo que intentes hacer).

Personalmente, no me gusta funciones que son independientes de la clase, pero a menudo se ven obligados a escribir funciones de esa manera cuando usted no tiene control sobre la clase, o no puedo cambiarlo por compatibilidad razones o lo que sea.

Realmente, creo que se trata de lo que es más importante para usted y de lo que realmente está tratando de hacer. Creo que tener la función como parte de la clase hace que sea más fácil de usar y más orientada a objetos, pero es mejor enclosure si restringes cuánto tiene acceso a los miembros privados de tu clase.

2

Si desea utilizarlo con otras clases no relacionadas, debería ser una función gratuita con anulaciones.

Por ejemplo, puede anular float Area(Hexagon const &) y float Diagonal(Hexagon const &). x, y, width y height significan diferentes cosas para diferentes formas.

Las clases base abstractas (o métodos homónimos no relacionados) son otra solución, pero las funciones gratuitas son extensibles por el usuario para que los usuarios obtengan una interfaz más uniforme una vez que hayan agregado sus propios elementos.

Evite las funciones gratuitas con nombres ambiguos, de modo que las funciones no relacionadas no se conviertan en anulaciones "competidoras".

Cuestiones relacionadas