Pregunta

oí nunca se debe tirar una cadena, porque hay una falta de información y podrás capturar las excepciones no se puede esperar a coger. ¿Cuáles son las buenas prácticas para lanzar excepciones? Cómo se hereda una clase de excepción base? ¿Tiene muchas excepciones o pocos? Qué haces MyExceptionClass y const o MyExceptionClass & etc. También sé excepciones nunca deben sido lanzados en los destructores

Voy a añadir que entiendo diseño por contrato y el momento de lanzar una excepción. Estoy pidiendo a la forma en que debería lanzar excepciones.

¿Fue útil?

Solución

En mi opinión, una función debe lanzar una excepción si no puede mantener su "promesa", si se tiene que romper su "contrato". la firma de la función (nombre y parámetros) determinar su contrato.

Teniendo en cuenta estas dos funciones miembro:

const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;

Los nombres de estas funciones, así como sus valores de retorno indican a mí que en el caso de FindApple la función es perfectamente capaz de devolver NULL cuando no se encontró el de manzana correcta, pero en el caso GetApple usted está esperando una manzana para volver. Si esa segunda función no puede cumplir su promesa, debe lanzar una excepción.

Excepciones son para esas condiciones excepcionales en las que una función no tiene otro modo de informar estas condiciones. Si decide que sea una parte de la promesa (léase: firma de la función). Entonces se puede informar que la condición sin lanzar una excepción

Tenga en cuenta que en el caso de FindApple , que depende de la persona que llama para decidir cómo manejar la condición de "no encontrar la derecha de Apple" porque ya no es una condición excepcional.

Usted puede verse tentado a tratar de evitar todas las excepciones, pero eso significa que tienes que tener en cuenta todas las posibles condiciones excepcionales, y que está colocando la carga sobre la persona que llama en su lugar. La persona que llama tiene que comprobar si hay "condiciones de error", entonces.

En última instancia, una excepción debe ser manejada, pero sólo por la persona que llama que sabe cómo manejar una condición particular de una manera útil . Y me refiero a esto en la más amplia interpretación posible: un servicio que se da por vencido intentará de nuevo más tarde, una interfaz de usuario que proporciona un mensaje de error útil, una aplicación web que presenta una pantalla "vaya", pero que se recupera muy bien, ... y así sucesivamente .

David

Otros consejos

Una cosa básica es la de reservar sólo excepciones para situaciones excepcionales. No los use para el control de flujo. Por ejemplo, "archivo no encontrado" no debe ser una excepción, debe ser un código de error o valor de retorno (a menos que el archivo es algo que debe existir, por ejemplo, un archivo de configuración). Pero si un archivo desaparece repentinamente mientras se está procesando, y luego lanzar una excepción es una buena opción.

Cuando excepciones se usan con moderación, no es necesario para convertir su código en un try-catch -spaghetti con el fin de evitar la recepción de excepciones incomprensibles en el contexto de las capas más profundas.

Utilice las excepciones estándar! Si usted tiene un error específico, tratar de evitar con valor de retorno. Si Tienes para utilizar las excepciones, definir su excepción personalizada que hereda de Excepción y crear un mensaje personalizado.

A veces puede ocurrir que usted no es capaz de devolver el código de error, por ejemplo. cuando se necesita contexto exacto de cuando la situación error ha ocurrido, por ejemplo. cuando se necesita para propagar el estado de error de 3 niveles de hasta -. le suelta contexto

En esta clase personalizada situación es la mejor solución. Yo uso este enfoque, definiendo mis propias clases en línea (no hay .cpp para ellos, y sólo .h), por ejemplo:.

class DeviceException {
    ;
}

class DeviceIOException: public DeviceException {
    DeviceIOException(std::string msg, int errorCode);
}

etc.

A continuación, puedo juzgar / actuar sobre la excepción por tipo y por la información contenida en su interior.

Siempre lanzar una excepción con un mensaje de donde ocurrió y lo que causó que sucediera:

throw NException("Foo::Bar", "Mungulator cause a stack overflow!");

A continuación, puede utilizar estas cadenas en los cuadros de mensajes, etc.

Siempre cojo a través de

catch (NException& ex) { ... }

Si se ejecuta ventanas se puede pasar el valor de error y tienen una función deducir el mensaje de error. El mejor ejemplo de esto es en Windows a través de C / C ++ por Jeffrey Richter .

Lanzar punteros no es probablemente una buena cosa, ya que complica la propiedad del objeto lanzado. Tipo de clase de excepciones son probablemente mejores que los fundamentos simplemente porque pueden contener más información sobre el motivo de la excepción.

En el uso de una jerarquía de clases o de clase, hay un par de puntos que debe tener en cuenta:

  1. Tanto el constructor de copia y destructor del objeto excepción Nunca debe lanzar una excepción. Si que hacen que estás programa se terminar inmediatamente. (ISO 15,5 / 1)

  2. Si los objetos de excepción tienen de base clases, a continuación, utilizar la herencia pública.
    Un controlador sólo se seleccionará para una derivado de clase base si la base clase es accesible . (ISO 15,3 / 3)

  3. Por último, (para todos los tipos de excepciones) garantizar que la expresión no puede ser lanzado sí dan lugar a una excepción de ser lanzado.

Por ejemplo:

class Ex {
public:
  Ex(int i) 
  : m_i (i)
  {
    if (i > 10) {
      throw "Exception value out of range";
    }
  }

  int m_i;
};


void foo (bool b) {
  if (! b) {
     // 'b' is false this is bad - throw an exception
     throw Ex(20);    // Ooops - throw's a string, not an Ex
  }
}

Siempre debe tirar una clase de excepción derivada de std :: excepción. Esto permite una cierta consistencia a su interfaz y permite una mayor flexibilidad a los clientes de estos métodos o funciones. Por ejemplo, si desea agregar una captura toda manejador que puede ser capaz de añadir un

bloque
catch(std::exception& e)
y hacerse con él. (Aunque a menudo no será capaz de salirse con la que si no se controla todo el código que puede lanzar).

tiendo a tirar únicas excepciones previstas por el estándar (es decir, std :: runtime_error), pero si usted quiere proporcionar granularidad adicional a sus manipuladores, usted debe sentirse libre para derivar su propio de std :: excepción. Ver de C ++ FAQ Lite .

Además, debe lanzar una temporal y cogerlo por referencia (para evitar la copia ctor ser invocada en su sitio de captura). Lanzar punteros también está mal visto, ya que no está claro quién debe limpiar la memoria. C ++ FAQ Lite se ocupa de esto también.

Para un proyecto actual, pensamos en la acción apropiada que podría ser tomada por el bucle principal del programa. El programa básico acepta mensajes XML, y guarda la información en una base de datos (con una buena cantidad de procesamiento en el medio).

  1. Los errores de datos que indican algo mal la entrada. La acción apropiada es salvar el mensaje a un directorio de registro, pero no procesarlo.
  2. Infraestructura errores que indican algunos subcomponente (como la cola de entrada, una base de datos SQL, una biblioteca JNI) no funciona correctamente. Dormir durante unos minutos y luego vuelva a conectar.
  3. errores
  4. de configuración que indican una cierta configuración de aspecto es inviable. Salir del programa.

El primer elemento es una excepción comprobada, ya que consideramos comprobación de los datos a ser parte de la interfaz de un método. Los otros son sin control desde el bucle principal no puede conocer las implementaciones de subcomponentes, por ejemplo una aplicación puede usar una base de datos SQL, o simplemente puede guardar los datos en la memoria -. la persona que llama no necesita saber

Si usted está lanzando excepciones de un componente de otros desarrolladores va a utilizar aguas abajo, por favor hacerles un gran favor y siempre derivar sus propias clases de excepción (si realmente los necesita c.f usando las excepciones estándar) de std :: excepción. Evitar a toda costa abominaciones pronuncian como tirar enteros, HRESULTS, char *, std :: string ...

Como se ha dicho ya utilizarlos sólo para situaciones excepcionales.

Siempre proporcionar una manera para el usuario para evitar lanzar una excepción, por ejemplo. si tiene método, que arrojará si algo va mal como esto:

public void DoSomethingWithFile() {
    if(!File.Exists(..))
        throw new FileNotFoundException();
}

proporcionar otro método para el usuario para llamar:

public bool CanDoSomething() {
    return File.Exists(..);
}

De esta manera la persona que llama no puede evitar excepciones si él quiere. No dude en tirar si algo está mal -. "A prueba rápida", pero siempre ofrecer camino libre de excepción

También mantener la jerarquía de clase de excepción plana y echar un vistazo a las excepciones estándar como InvalidStateException y ArgumentNullExcpetion.

Aquí está un ejemplo simple de lanzar una excepción que tiene apenas ninguna recursos:

class DivisionError {};
class Division
{
public:
    float Divide(float x, float y) throw(DivisionError)
    {
        float result = 0;
        if(y != 0)
            result = x/y;
        else
            throw DivisionError();
        return result;
    }
};

int main()
{
    Division d;
    try
    {
        d.Divide(10,0);
    }
    catch(DivisionError)
    {
        /*...error handling...*/
    }
}

La clase vacío que se produce no toma ningún recurso o muy pocos ...

Desde el C ++ FAQ, [17.12] ¿Qué debo tirar ? :

  

En general, lo mejor es tirar objetos, no empotrados.   Si es posible, usted debe lanzar instancias de clases que   derivar (en última instancia) de la clase std::exception.

... y

  

La práctica más común es lanzar un temporal:    (véase el ejemplo que sigue)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top