Domande sulle eccezioni C ++ sul rilancio dell'eccezione originale


117

Il seguente append () nel catch farà sì che l'eccezione rilanciata veda l'effetto della chiamata di append ()?

try {
  mayThrowMyErr();
} catch (myErr &err) {
  err.append("Add to my message here");
  throw; // Does the rethrow exception reflect the call to append()?
}

Allo stesso modo, se lo riscrivo in questo modo, si verificherà il bit slicing se l'eccezione effettiva è derivata da myErr?

try {
  mayThrowObjectDerivedFromMyErr();
} catch (myErr &err) {
  err.append("Add to my message's base class here");
  throw err; // Do I lose the derived class exception and only get myErr?
}

Risposte:


150

In entrambi i casi, poiché catturi per riferimento, stai effettivamente alterando lo stato dell'oggetto eccezione originale (che puoi pensare come residente in una posizione di memoria magica che rimarrà valida durante il successivo svolgimento - 0x98e7058nell'esempio sotto). Però,

  1. Nel primo caso, dal momento che si rigenerare con throw;(che, a differenza throw err;, conserva l'oggetto eccezione originale, con le modifiche, in detto "luogo magico" a 0x98e7058) si riflettono la chiamata a append ()
  2. Nel secondo caso, dal momento che lanci qualcosa in modo esplicito, verrà creata una copia di errverrà quindi lanciata di nuovo (in una diversa "posizione magica" 0x98e70b0- perché per quanto ne sa il compilatore errpotrebbe essere un oggetto sullo stack in procinto di essere srotolato, come eera at 0xbfbce430, non nella "posizione magica" at 0x98e7058), quindi perderai i dati specifici della classe derivata durante la costruzione della copia di un'istanza della classe base.

Semplice programma per illustrare cosa sta succedendo:

#include <stdio.h>

struct MyErr {
  MyErr() {
    printf("  Base default constructor, this=%p\n", this);
  }
  MyErr(const MyErr& other) {
    printf("  Base copy-constructor, this=%p from that=%p\n", this, &other);
  }
  virtual ~MyErr() {
    printf("  Base destructor, this=%p\n", this);
  }
};

struct MyErrDerived : public MyErr {
  MyErrDerived() {
    printf("  Derived default constructor, this=%p\n", this);
  }
  MyErrDerived(const MyErrDerived& other) {
    printf("  Derived copy-constructor, this=%p from that=%p\n", this, &other);
  }
  virtual ~MyErrDerived() {
    printf("  Derived destructor, this=%p\n", this);
  }
};

int main() {
  try {
    try {
      MyErrDerived e;
      throw e;
    } catch (MyErr& err) {
      printf("A Inner catch, &err=%p\n", &err);
      throw;
    }
  } catch (MyErr& err) {
    printf("A Outer catch, &err=%p\n", &err);
  }
  printf("---\n");
  try {
    try {
      MyErrDerived e;
      throw e;
    } catch (MyErr& err) {
      printf("B Inner catch, &err=%p\n", &err);
      throw err;
    }
  } catch (MyErr& err) {
    printf("B Outer catch, &err=%p\n", &err);
  }
  return 0;
}

Risultato:

  Base default constructor, this=0xbfbce430
  Derived default constructor, this=0xbfbce430
  Base default constructor, this=0x98e7058
  Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
  Derived destructor, this=0xbfbce430
  Base destructor, this=0xbfbce430
A Inner catch, &err=0x98e7058
A Outer catch, &err=0x98e7058
  Derived destructor, this=0x98e7058
  Base destructor, this=0x98e7058
---
  Base default constructor, this=0xbfbce430
  Derived default constructor, this=0xbfbce430
  Base default constructor, this=0x98e7058
  Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
  Derived destructor, this=0xbfbce430
  Base destructor, this=0xbfbce430
B Inner catch, &err=0x98e7058
  Base copy-constructor, this=0x98e70b0 from that=0x98e7058
  Derived destructor, this=0x98e7058
  Base destructor, this=0x98e7058
B Outer catch, &err=0x98e70b0
  Base destructor, this=0x98e70b0

Vedi anche:


24

Questa domanda è piuttosto vecchia e ha una risposta adeguata al momento in cui è stata posta. Tuttavia, voglio solo aggiungere una nota su come gestire correttamente le eccezioni da C ++ 11 e credo che ciò corrisponda molto bene a ciò che stavi cercando di ottenere con la tua funzione di aggiunta:

Usa std::nested_exceptionestd::throw_with_nested

È descritto su StackOverflow qui e qui , come puoi ottenere un backtrace sulle tue eccezioni all'interno del tuo codice senza bisogno di un debugger o di un log complicato, semplicemente scrivendo un gestore di eccezioni appropriato che rilancerà le eccezioni nidificate.

Dato che puoi farlo con qualsiasi classe di eccezione derivata, puoi aggiungere molte informazioni a tale backtrace! Puoi anche dare un'occhiata al mio MWE su GitHub , dove un backtrace sarebbe simile a questo:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

8

Sì, il rilancio rigira l'oggetto eccezione originale, che hai modificato da un riferimento. Puoi anche catturare un riferimento alla classe base, modificarlo ed essere ancora in grado di rilanciare il tipo di eccezione derivato originale di throw;.


1

per prima domanda, sì.

ma per il secondo, fare riferimento alla risposta di Vlad. dovrai progettare attentamente il tuo oggetto eccezione per gestire il copy ctor. per convenzione, la classe base non riconosce la sua figlia, quindi molto probabilmente perderai i dati aggiuntivi trasportati dalla classe derivata.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.