2012-05-08 28 views
5

He visto muchos códigos donde los codificadores definen una función init() para las clases y lo llaman lo primero después de crear la instancia.Inicializando en el Constructor

¿Hay algún daño o limitación de hacer todas las inicializaciones en Constructor?

+2

¿Por qué está etiquetado con Java y C++? ¿Deliberar? –

+1

no, pero a veces se invoca init para inicializar después de dos constructores diferentes, para evitar el pegado del código. – Anycorn

+0

virtual init() se puede utilizar para la inicialización polimórfica – vid

Respuesta

0

Es un patrón de diseño que tiene que ver con excepciones arrojado desde el interior de un constructor de objetos.

En C++ si se lanza una excepción desde el interior de un objeto costructor, dicho objeto se considera no construido en absoluto por el tiempo de ejecución del idioma. Como consecuencia, no se llamará al destructor de objetos cuando el objeto se salga del alcance.

Esto significa que si usted tenía un código como éste dentro de su constructor:

int *p1 = new int; 
int *p2 = new int; 

y el código como este en su destructor:

delete p1; 
delete p2; 

y la inicialización de p2 dentro del constructor falla debido a que no hay más memoria disponible, se lanza una excepción bad_alloc por el nuevo operador. En ese punto, su objeto no está completamente construido, incluso si la memoria para p1 ha sido asignada correctamente. Si esto sucede, no se invocará el destructor y tendrá una fuga de p1.

Por lo tanto, cuanto más código coloque dentro del constructor, mayor será la probabilidad de que se produzca un error que pueda ocasionar pérdidas de memoria.

Esa es la razón principal para esa elección de diseño, que no es una locura después de todo.

Más sobre esto en el blog de Herb Sutter: Constructors exceptions in C++

0

Es una opción de diseño. Desea mantener su constructor lo más simple posible, por lo que es fácil leer lo que está haciendo. Por eso, a menudo verá constructores que llaman a otros métodos o funciones, según el idioma. Le permite al programador leer y seguir la lógica sin perderse en el código.

Como van los constructores, puede encontrar rápidamente un escenario en el que tiene una tremenda secuencia de eventos que desea desencadenar. El buen diseño exige que descomponga estas secuencias en métodos más simples, una vez más, para que sea más legible y más fácil de mantener en el futuro.

Entonces, no, no hay daño o limitación, es una preferencia de diseño. Si necesita toda esa inicialización hecha en el constructor, hágalo allí. Si solo lo necesita más tarde, póngalo en un método que llame más tarde. De cualquier forma, depende totalmente de usted y no existen reglas estrictas o rápidas al respecto.

2

Por lo general, para el mantenimiento y reducir el tamaño del código cuando varios constructores llaman el mismo código de inicialización:

class stuff 
{ 
public: 
    stuff(int val1) { init(); setVal = val1; } 
    stuff()   { init(); setVal = 0; } 

    void init()  { startZero = 0; } 

protected: 
    int setVal; 
    int startZero; 
}; 
1

En Java, hay buenas razones para mantener a los constructores a corto y moviendo la lógica de inicialización en un método init():

  • constructores no se heredan, por lo que cualquier subclases que sea reimplementar ellos o proporcionar talones de esa cadena con super
  • no debe llamar a métodos reemplazables en un constructor ya que se puede encontrar su objeto en un estado incoherente en el que se ha inicializado parcialmente
+0

Ambos argumentos parecen argumentar en contra de utilizar un método 'init()'. El uso de 'super' deja en claro que está inicializando la clase base,' init' podría anularse. Y el hecho de que 'init' pueda definirse en una clase derivada es un argumento muy, muy fuerte contra su uso en una clase base. –

2

Todo lo contrario: por lo general es mejor poner todas las inicializaciones en el constructor. En C++, la "mejor" política suele ser poner las inicializaciones en una lista de inicializadores, de modo que los miembros sean construidos directamente con los valores correctos, en lugar de predeterminados, construidos y asignados. En Java, desea evitar una función (a menos que sea private o final), ya que la resolución dinámica puede ponerle en un objeto que no se ha inicializado.

La única razón por la que usaría una función init() es porque tiene una gran cantidad de constructores con características comunes significativas. (En el caso de C++, todavía habría que sopesar la diferencia entre el valor predeterminado construcción, a continuación, la asignación vs. construcción inmediata con el valor correcto .)

0

Si tiene varios objetos de la misma clase o de diferentes clases que necesitan ser inicializado con los punteros entre sí, por lo que hay por lo menos un ciclo de puntero dependencia, no puede hacer toda la inicialización solo en constructores. (¿Cómo vas a construir el primer objeto con un puntero/referencia a otro objeto cuando el otro objeto aún no se ha creado?)

Una situación donde esto puede suceder fácilmente es en un sistema de simulación de eventos donde los diferentes componentes interactuar, de modo que cada componente necesita punteros a otros componentes.

Dado que es imposible hacer toda la inicialización en los constructores, al menos parte de la inicialización debe ocurrir en las funciones de inicio. Eso lleva a la pregunta: ¿Qué partes deben hacerse en las funciones de inicio? La opción flexible parece ser hacer toda la inicialización del puntero en las funciones de inicio. Entonces, puede construir los objetos en cualquier orden, ya que al construir un objeto dado, no tiene que preocuparse de si ya tiene los punteros necesarios para los otros objetos que necesita conocer.