No, lanciare un'eccezione è il modo migliore per segnalare un errore durante la costruzione dell'oggetto. (. Dal momento che non c'è alcun valore di ritorno, non c'è altro modo, diverso la costruzione di un oggetto senza testa, che è cattivo stile in C++)
Da l'uomo stesso, Bjarne Stroustrup: http://www.stroustrup.com/bs_faq2.html#ctor-exceptions
Re: "Ma il mio distruttore non è stato chiamato "
Infatti. In C++ si dice che la durata di un oggetto inizia quando il costruttore esegue il completamento. E finisce proprio quando viene chiamato il distruttore. Se il ctor lancia, il dtor non viene chiamato.
(Ma dtors di eventuali oggetti variabili membro, la cui ctors già corse fino al completamento prima questo Ran ctor, sono chiamati.)
Si consiglia di consultare la norma, o di un libro di testo buona per maggiori dettagli, esp. in relazione a ciò che accade quando è coinvolta l'ereditarietà. Come regola generale, i distruttori sono chiamati nell'ordine inverso di costruzione.
La tua domanda sul motivo per cui "~ B" non è stato chiamato nel codice specifico, è perché non si cattura l'eccezione in main. Se cambi il tuo codice in modo che il principale rilevi l'eccezione, verrà chiamato "~ B()". Ma quando viene lanciata un'eccezione che non ha catture, l'implementazione è libera di terminare il programma senza chiamare distruttori o distruggere oggetti inizializzati staticamente.
riferimento a 11 C++ standard (sottolineatura mia):
15.5.1 La std :: terminate() funzione [except.terminate]
In alcuni le situazioni che gestiscono le eccezioni devono essere abbandonate per tecniche di gestione degli errori meno sottili.
...
In questi casi, std :: terminate() è chiamato (18.8.3). Nella situazione in cui non viene trovato alcun gestore corrispondente, viene definita l'implementazione indipendentemente dal fatto che lo stack venga svolto prima che std :: terminate() venga chiamato.
Hai lanciato un'eccezione e non l'hai catturato, quindi il programma è stato interrotto. Cosa ti fa pensare che sia UB? – Beta