2010-04-27 14 views
5

Según tengo entendido, cuando una nueva clase se instancia en C++, se devuelve un puntero a la nueva clase, o NULL, si no hay memoria suficiente. Estoy escribiendo una clase que inicializa una lista vinculada en el constructor. Si hay un error al inicializar la lista, me gustaría que el instalador de clases devuelva NULL.¿Puedo causar que falle una nueva instancia de la clase C++ si no se cumplen ciertas condiciones en el constructor?

Por ejemplo:

MyClass * pRags = new MyClass;

Si la lista enlazada en el constructor MiClase no puede inicializar correctamente, me gustaría PRAGS igual a NULL. Sé que puedo usar banderas y controles adicionales para hacer esto, pero me gustaría evitar eso, si es posible.
¿Alguien sabe de una manera de hacer esto?

Respuesta

16

El enfoque común aquí es arrojar una excepción (y manejarla en algún lugar más arriba).

Uno de los beneficios del mecanismo de excepción es que le permite lanzar una excepción desde dentro de un constructor de clase. En ese caso, nunca se llega a la situación en la que se devuelve un puntero al inválido. Usted "obtendrá el control" en el bloque catch correspondiente. Si el puntero solo fue declarado dentro del bloque try (o en algún otro método invocado por el bloque try), estaría fuera de su alcance en ese bloque catch.

Eso no es un truco, es bastante legítimo y una técnica de programación común. Por ejemplo, si el constructor de su clase asigna memoria dinámicamente (por ejemplo, para un búfer interno) y esta asignación falló, le conviene lanzar una excepción porque al final del cuerpo del constructor no tendría un objeto válido.

Aquí es un ejemplo (que he estado haciendo de Java durante los últimos 10 años, por lo que mi código C++ a continuación está probablemente en mal estado aquí, tal vez alguien puede editar esto para mí)

// Begin C++ code written by a Java programmer... :) 
class Myclass 
{ 
    public: 
     Myclass(int length) 
     { 
      if(length<=0) throw BadBufferSizeException("Bla bla bla");   
      this->buffer = (char*)malloc(length*sizeof(char)); // don't remember the new syntax 
     } 

     void doSomething() 
     { 
      // Code for placing stuff in the buffer 
     } 
    private: 
     char* buffer; 
}; 


int main() 
{ 
    try 
    { 
    int len; 
    len = getLengthFromUser(); 
    MyClass* pMyClass = new MyClass(len); 
    myClass->doSomething(); 
    } catch(const Exception & e) 
    { 
     // Whatever... Note how pMyClass is not even accessible here 
    } 
} 

Tenga en cuenta que si usted definió pMyclass como nulo fuera del bloque try, y luego solo lo reasignó dentro del bloque try cuando crea la clase; en el caso de falla, es probable que aún tenga nulo, pero nunca habría ejecutado doSomething(). Si le preocupa la inicialización, también puede mover la llamada a DoSomething() fuera del bloque try-catch, pero querrá asegurarse de que su puntero no sea nulo.

También tenga en cuenta que C++ le da más (¿demasiado?) Libertad a la hora de tirar cosas que otros idiomas. Normalmente me gusta tener una jerarquía de clases de excepción o utilizar una biblioteca existente con dicha jerarquía.

+1

Esto es también lo que las preguntas frecuentes de C++ recomiendan http://www.parashift.com/c++faq-lite/exceptions.html#faq-17.2 –

+0

Gracias, Uri. Entonces, ¿debería hacer algo como 'try {if (!Esto()) tira 20; } 'en el constructor y omite la instrucción' catch', ¿o es necesaria la instrucción 'catch'? –

+0

@Jim: Permítanme editar mi respuesta con un ejemplo – Uri

1

La forma estándar para que un constructor indique un error es con una excepción.

Si, por alguna razón, su aplicación no puede hacer frente a las necesidades de excepciones y el valor de retorno es NULL, se puede envolver el constructor en el método de fábrica como esta ...

class MyClass 
{ 
    public: 
     static MyClass* makeMyClass()   
     { 
      MyClass* retval = NULL; 
      try 
      { 
       retval = new MyClass(); 
      } 
      catch(std::exception& e) 
      { 
       // Report the exception; 
       retval = NULL; 
      } 
      return retval; 
     } 
    // .... 
    }; 
+0

Esta es una buena alternativa si no quiero que tus clientes tengan que manejar la excepción. –

0

Su comprensión es un poco viejo , ya que la Norma de 1995 prescribía el uso de excepciones en lugar de devolver 0. También proporcionaba una serie de métodos nothrow, que funciona de la siguiente manera: Foo * foo = new (nothrow) Foo;.

+0

Creo que se está refiriendo a la falla del constructor, no a la asignación de memoria para el nuevo objeto que falla. –

+0

Solo para ser claro, nunca * desde * 1995 que ha sido así hecho. 'std :: bad_alloc' no fue obsoleto hace 15 años. – Potatoswatter

Cuestiones relacionadas