2010-06-22 13 views
5

Sé que esta pregunta tiene un título similar a esto: C++: calling member functions within constructor? pero estoy haciendo una pregunta más general.Llamar a las funciones miembro desde un constructor

¿Es una buena práctica llamar a las funciones miembro desde dentro de un constructor? Hace que sea más fácil leer el código y prefiero la manera de hacerlo del tipo de encapsulación (es decir, cada bloque de código tiene un único objetivo).

Un ejemplo ilustrativo, en python:

class TestClass: 
    def __init__(self): 
     self.validate() 

    def validate(self): 
     # this validates some data stored in the class 

¿Es esta una mejor forma de hacerlo que escribir el código validate dentro del constructor? ¿Hay inconvenientes en este método? Por ejemplo, ¿es más costoso con la sobrecarga de la función?

Personalmente lo prefiero para la legibilidad, pero esa es solo mi preferencia.

Saludos

Respuesta

5

No creo que haya nada intrínsecamente incorrecto en llamar a las funciones miembro de un constructor , siempre que no sean funciones virtuales.

El problema de llamar a funciones miembro virtuales desde un constructor es que una subclase puede anular la función. Esto hará que el constructor llame a la implementación anulada en la subclase, antes de que se haya llamado al constructor para la parte de la subclase del objeto.

En Java, cualquiera de los modificadores de acceso finales privada, estática o hará que el método seguro para llamar a un constructor mediante la prevención de una llamada virtual para el método de la superclase. No creo que estas técnicas estén disponibles en Python.

+0

realidad, llamar a los métodos virtuales desde un constructor puede ser muy útil. Por ejemplo, un método virtual para recuperar un tipo de clase específico para crear instancias en el constructor base donde la clase específica es un descendiente de una clase base conocida o el implementador de una interfaz conocida. De esta forma, puede mantener la creación de instancias y la destrucción en la clase base, al tiempo que permite una composición personalizada por parte de los descendientes. –

+0

¿Recomiendas "llamar a un método en un objeto no inicializado" como una buena práctica para uso general en todos los idiomas? Preferiría usar los patrones de diseño AbstractFactory y/o Builder para resolver su problema de ejemplo. – richj

0

estoy más familiarizado con C++ que Python, pero no veo ningún problema con llamar a funciones miembro de constructores, especialmente cuando esta práctica es capaz de factorizar un código similar de varios constructores. Cualquier cosa que reduzca la redundancia es buena en mis libros.

0

Desde el punto de vista de la legibilidad, es definitivamente mejor. Una cosa que quizás deba preguntarse aquí es si el método de validación puede ejecutarse después de el objeto se inicializa. Si ese no es el caso, puede a) utilizar algún tipo de variable privada inicializada o ob) usar el Builder pattern para obtener sus objetos en un estado válido antes de usarlos.

Asegúrate de que la función sea privada. No desea meterse con las subclases anulándolas (a menos que esto sea deseado por diseño, en cuyo caso haga que sea abstracto/virtual).

0

El problema principal con esto es que la función de miembro tiene que trabajar con un objeto que puede ser parcialmente inicializado. Y si (incluso accidentalmente) pasa una referencia al objeto en otro lugar, otro código tiene que od el mismo. Esto puede ser bastante confuso y propenso a errores, especialmente una vez que comience a anular dicha función en una subclase.

Por lo tanto, en general, esta práctica debe evitarse o al menos limitarse a funciones que no pueden ser anuladas, y nunca deben pasar una referencia al objeto que se está construyendo para cualquier otro código.

2

Hay al menos uno asociado "Gotcha" se debe tener en cuenta:

N3797 12.6.2/14

funciones miembros (incluyendo las funciones miembro virtuales, 10.3) puede ser llamado para una objeto en construcción. Del mismo modo, un objeto en construcción puede ser el operando del operador typeid (5.2.8) o de un dynamic_cast (5.2.7). Sin embargo, si estas operaciones se llevan a cabo en un ctor-inicializador (o en una función llamada directa o indirectamente de un ctor-inicializador) delante de toda la mem-inicializador han completado s de clases base, el resultado de la operación no está definida [Ejemplo:

class A { 
public: 
    A(int); 
}; 

class B : public A { 
    int j; 
public: 
    int f(); 
    B() : A(f()), // undefined: calls member function 
        // but base A not yet initialized 
    j(f()) { }  // well-defined: bases are all initialized 
}; 

class C { 
public: 
    C(int); 
}; 

class D : public B, C { 
    int i; 
public: 
    D() : C(f()), // undefined: calls member function 
        // but base C not yet initialized 
    i(f()) { } // well-defined: bases are all initialized 
}; 

- ejemplo extremo]

Cuestiones relacionadas