C++ Exceções (try e catch)
Nem tudo dá certo: o arquivo não existe, o usuário digitou "abc" onde devia ir um número, a conta não tem saldo para o saque. Uma função que encontra um problema desses muitas vezes não sabe o que fazer com ele (mostrar uma mensagem? tentar de novo? desistir?), mas quem a chamou sabe. As exceções são o jeito de o C++ levar o problema de quem o encontrou até quem sabe tratá-lo. Nesta aula você vai lançar erros com throw, pegá-los com try e catch e ver o que acontece quando ninguém pega.
- Como lançar um erro:
throw std::invalid_argument("…");. - Como pegar o erro e continuar:
try { … } catch (const std::exception& e) { … }. - O que acontece quando ninguém pega:
terminate called after throwing an instance of …. - A família
std::exceptione as filhas mais usadas:runtime_error,invalid_argument,out_of_range. - Como ler a mensagem com
e.what(), e por que pegar porconst&. - Como usar vários
catch, a ordem certa entre eles e ocatch (...). - Como criar a sua própria exceção:
class SaldoInsuficiente : public std::runtime_error.
Todos os programas desta aula foram compilados com o g++ 15 na máquina fictícia da Ana, com o comando padrão do guia: g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa. Você pode repetir cada um no editor do guia.
A ideia: o alarme de incêndio
Num prédio, quem vê fumaça no 7º andar não precisa saber apagar incêndio. A pessoa aperta o alarme, e o aviso sobe até quem sabe agir: a brigada, os bombeiros. Enquanto isso, todo mundo para o que estava fazendo.
| No prédio | Em C++ |
|---|---|
| Ver a fumaça e apertar o alarme | throw ("lançar"): a função encontra o problema e lança uma exceção. |
| O aviso que sobe (com o andar e o que está pegando fogo) | O objeto exceção, com uma mensagem dentro. |
| A área vigiada pela brigada | try ("tentar"): um bloco de código cujos alarmes você quer atender. |
| A brigada que atende | catch ("pegar"): o código que trata o problema. |
| Ninguém atende o alarme | O programa é abortado. |
1. throw, try e catch
#include <iostream>
#include <stdexcept>
double dividir(double a, double b) {
if (b == 0) {
throw std::invalid_argument("divisão por zero");
}
return a / b;
}
int main() {
try {
std::cout << dividir(10, 4) << '\n';
std::cout << dividir(10, 0) << '\n';
std::cout << "Esta linha não aparece.\n";
} catch (const std::invalid_argument& e) {
std::cout << "Erro: " << e.what() << '\n';
}
std::cout << "O programa continua.\n";
}
$ g++ -std=c++23 -Wall -Wextra dividir.cpp -o dividir $ ./dividir 2.5 Erro: divisão por zero O programa continua.
- #include <stdexcept>
- Traz as exceções padrão (standard exceptions), como
std::invalid_argument. - throw std::invalid_argument("divisão por zero");
- Cria uma exceção do tipo
std::invalid_argument("argumento inválido"), com a mensagem entre parênteses, e a lança. A função para ali mesmo: oreturn a / b;não roda. - try { … }
- O bloco vigiado. Se qualquer coisa lá dentro lançar uma exceção, inclusive uma função chamada lá dentro, o resto do bloco é pulado. Por isso
Esta linha não aparece.não apareceu. - catch (const std::invalid_argument& e)
- Diz qual tipo de exceção este bloco atende e dá um nome a ela:
e. Oconst&pega a exceção original, sem copiar (seção 4). - e.what()
- Devolve a mensagem que foi colocada na exceção (what = "o quê": "o que aconteceu?").
- O programa continua.
- Depois do
catch, o programa segue normalmente pela linha de baixo. O erro foi tratado.
Repare na ordem dos acontecimentos: dividir(10, 4) deu certo e mostrou 2.5; dividir(10, 0) lançou; o resto do try foi pulado; o catch rodou; o programa seguiu.
A exceção atravessa funções
O grande poder das exceções: a função que lança e o catch que trata podem estar longe um do outro. A exceção "sobe" pelas funções, pulando o resto de cada uma, até achar um try:
#include <iostream>
#include <stdexcept>
void c() {
std::cout << "c: começou\n";
throw std::runtime_error("problema em c");
std::cout << "c: terminou\n";
}
void b() {
std::cout << "b: começou\n";
c();
std::cout << "b: terminou\n";
}
int main() {
try {
b();
} catch (const std::exception& e) {
std::cout << "main pegou: " << e.what() << '\n';
}
}
$ g++ -std=c++23 -Wall -Wextra fluxo.cpp -o fluxo $ ./fluxo b: começou c: começou main pegou: problema em c « "c: terminou" e "b: terminou" não aparecem
O main chamou b, que chamou c, que lançou. Nem c nem b tinham try, então as duas foram interrompidas no meio, e a exceção chegou ao main. As variáveis locais das funções interrompidas são destruídas direitinho no caminho: um arquivo aberto em b, por exemplo, seria fechado.
2. Quando ninguém pega a exceção
E se não houver try nenhum? O mesmo programa da divisão, sem o try:
#include <iostream>
#include <stdexcept>
double dividir(double a, double b) {
if (b == 0) {
throw std::invalid_argument("divisão por zero");
}
return a / b;
}
int main() {
std::cout << dividir(10, 4) << '\n';
std::cout << dividir(10, 0) << '\n';
std::cout << "Fim.\n";
}
$ g++ -std=c++23 -Wall -Wextra semtry.cpp -o semtry $ ./semtry 2.5 terminate called after throwing an instance of 'std::invalid_argument' what(): divisão por zero Abortado (imagem do núcleo gravada) ./semtry $ echo $? 134
Linha por linha:
| Linha | Quem escreveu | O que significa |
|---|---|---|
terminate called after throwing an instance of 'std::invalid_argument' | A biblioteca do C++ | "terminate foi chamado depois do lançamento de uma std::invalid_argument". A exceção subiu até sair do main, e o C++ chamou a função de emergência std::terminate, que encerra o programa. |
what(): divisão por zero | A biblioteca do C++ | A mensagem de dentro da exceção, o mesmo texto que e.what() devolveria. É a pista mais útil. |
Abortado (imagem do núcleo gravada) ./semtry | O Bash | O programa foi abortado (morto pelo sinal SIGABRT). "Imagem do núcleo gravada" (core dumped, num sistema em inglês) quer dizer que o sistema pode ter salvo uma cópia da memória do programa para análise. |
134 | O Bash (echo $?) | O código de saída: 128 + 6, onde 6 é o número do sinal SIGABRT. Sempre que você vir 134, pense "abortado". |
As duas primeiras linhas saem em inglês em qualquer sistema: são escritas pela biblioteca do C++. A terceira vem do terminal e segue o idioma do sistema.
O editor usa o Clang com outra biblioteca do C++ (a libc++, do projeto LLVM; o g++ usa a libstdc++, do projeto GNU). O mesmo programa termina lá com uma linha só: libc++abi: terminating due to uncaught exception of type std::invalid_argument: divisão por zero ("encerrando por causa de uma exceção não pega do tipo…"), e o código de saída também é 134. Palavras diferentes, mesma informação: o tipo da exceção e a mensagem.
cout podem sumir quando o programa aborta
No terminal, o 2.5 apareceu antes do erro. Mas se a saída do programa for para um arquivo ou outro programa (./semtry > saida.txt), o cout guarda o texto num buffer e o aborto pode impedir que ele seja escrito. Mais um motivo para tratar as exceções.
Missão: rode este programa e veja ele abortar na turma B, que está vazia. Coloque as quatro linhas das médias dentro de um try e trate a std::invalid_argument com um catch que mostra Erro: seguido de e.what(). No fim, o programa deve mostrar Fim. e terminar com código 0.
#include <iostream>
#include <stdexcept>
#include <vector>
double media(const std::vector<double>& notas) {
if (notas.empty()) {
throw std::invalid_argument("lista vazia");
}
double soma = 0.0;
for (double n : notas) {
soma += n;
}
return soma / notas.size();
}
int main() {
std::vector<double> turma_a = {8.5, 6.0, 9.5};
std::vector<double> turma_b = {};
double a = media(turma_a);
std::cout << "Turma A: " << a << '\n';
double b = media(turma_b);
std::cout << "Turma B: " << b << '\n';
std::cout << "Fim.\n";
}
#include <iostream>
#include <stdexcept>
#include <vector>
double media(const std::vector<double>& notas) {
if (notas.empty()) {
throw std::invalid_argument("lista vazia");
}
double soma = 0.0;
for (double n : notas) {
soma += n;
}
return soma / notas.size();
}
int main() {
std::vector<double> turma_a = {8.5, 6.0, 9.5};
std::vector<double> turma_b = {};
try {
double a = media(turma_a);
std::cout << "Turma A: " << a << '\n';
double b = media(turma_b);
std::cout << "Turma B: " << b << '\n';
} catch (const std::invalid_argument& e) {
std::cout << "Erro: " << e.what() << '\n';
}
std::cout << "Fim.\n";
}
3. A família std::exception
Você não precisa inventar tipos de erro: a biblioteca padrão traz uma família pronta. Todas descendem de std::exception, a "mãe" de todas, e cada filha diz que tipo de problema aconteceu:
std::exception « a mãe de todas (<exception>)
├── std::logic_error « erro de lógica: o programa pediu algo sem sentido
│ ├── std::invalid_argument « argumento inválido: stoi("abc"), divisão por zero
│ ├── std::out_of_range « fora da faixa: v.at(10) num vetor de 3, número grande demais
│ └── std::length_error « tamanho grande demais para um container
├── std::runtime_error « erro "do mundo lá fora", só detectável rodando
│ └── std::filesystem::filesystem_error « (neta, via std::system_error) arquivo não existe…
└── std::bad_alloc « acabou a memória (new falhou)
A biblioteca padrão já lança várias delas. Você viu algumas em aulas anteriores:
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
int main() {
std::vector<int> v = {10, 20, 30};
try {
std::cout << std::stoi("42") << '\n';
std::cout << std::stoi("quarenta") << '\n';
} catch (const std::invalid_argument& e) {
std::cout << "invalid_argument: " << e.what() << '\n';
}
try {
std::cout << std::stoi("99999999999") << '\n';
} catch (const std::out_of_range& e) {
std::cout << "out_of_range: " << e.what() << '\n';
}
try {
std::cout << v.at(10) << '\n';
} catch (const std::out_of_range& e) {
std::cout << "out_of_range: " << e.what() << '\n';
}
}
$ g++ -std=c++23 -Wall -Wextra biblioteca.cpp -o biblioteca $ ./biblioteca 42 invalid_argument: stoi out_of_range: stoi out_of_range: vector::_M_range_check: __n (which is 10) >= this->size() (which is 3)
| Quem lança | Quando | Exceção | what() no g++ |
|---|---|---|---|
std::stoi("quarenta") | O texto não começa com número | std::invalid_argument | stoi |
std::stoi("99999999999") | O número não cabe num int | std::out_of_range | stoi |
v.at(10) | Posição que não existe (veja std::vector) | std::out_of_range | vector::_M_range_check: … |
std::filesystem::file_size | Arquivo inexistente (veja Arquivos) | filesystem_error | filesystem error: cannot get file size: … |
As mensagens do what() da biblioteca são curtas e técnicas (no g++, o stoi só diz o próprio nome; no editor do guia, com a libc++, ele diz stoi: no conversion, "nenhuma conversão"). Elas servem para o programador. Para o usuário, mostre uma mensagem sua, em português, como nos exemplos abaixo.
Qual lançar nas suas funções
std::invalid_argument: quem chamou passou um valor que a função não aceita (divisor zero, idade negativa).std::out_of_range: um número ou posição fora dos limites permitidos.std::runtime_error: algo deu errado "lá fora", e não por culpa de quem chamou (arquivo corrompido, servidor fora do ar). Na dúvida, é esta.
4. Pegar por const&
Em todo catch desta aula, a exceção é pega por referência constante: catch (const std::exception& e). Os motivos:
- & (referência): você recebe a exceção original, sem cópia, e com o tipo verdadeiro dela. Quem pega uma
std::exceptionpor referência e recebe umaruntime_errorcontinua vendo a mensagem certa. - const: tratar o erro não deve alterá-lo.
Veja o que acontece ao pegar por valor (sem o &):
#include <iostream>
#include <stdexcept>
int main() {
try {
throw std::runtime_error("o servidor não respondeu");
} catch (std::exception e) {
std::cout << e.what() << '\n';
}
}
$ g++ -std=c++23 -Wall -Wextra porvalor.cpp -o porvalor
porvalor.cpp: In function ‘int main()’:
porvalor.cpp:7:29: warning: catching polymorphic type ‘class std::exception’ by value [-Wcatch-value=]
7 | } catch (std::exception e) {
| ^
$ ./porvalor
std::exception « a mensagem "o servidor não respondeu" se perdeu!
O g++ avisou: catching polymorphic type … by value = "pegando um tipo polimórfico por valor". Ao copiar a runtime_error para uma variável do tipo std::exception, só a "parte mãe" foi copiada, e a mensagem ficou para trás. Esse corte tem nome: fatiamento (slicing; você vai entendê-lo em Polimorfismo). Com const std::exception& e, o programa mostraria o servidor não respondeu.
5. Vários catch
Um try pode ter vários catch, um para cada tipo de problema. O C++ testa de cima para baixo e usa o primeiro que servir:
#include <iostream>
#include <stdexcept>
#include <string>
int ler_idade(const std::string& texto) {
int idade = std::stoi(texto);
if (idade < 0 || idade > 130) {
throw std::out_of_range("idade fora da faixa: " + texto);
}
return idade;
}
int main() {
for (std::string texto : {"29", "abc", "200", "99999999999"}) {
try {
int idade = ler_idade(texto);
std::cout << texto << " -> ok, " << idade << " anos\n";
} catch (const std::invalid_argument& e) {
std::cout << texto << " -> não é número (" << e.what() << ")\n";
} catch (const std::out_of_range& e) {
std::cout << texto << " -> fora da faixa (" << e.what() << ")\n";
}
}
}
$ g++ -std=c++23 -Wall -Wextra varios.cpp -o varios $ ./varios 29 -> ok, 29 anos abc -> não é número (stoi) 200 -> fora da faixa (idade fora da faixa: 200) 99999999999 -> fora da faixa (stoi)
Repare que o out_of_range veio de dois lugares: do throw da própria ler_idade (idade 200) e de dentro do std::stoi (número enorme). O catch não se importa com quem lançou, só com o tipo.
Pegar qualquer filha: catch (const std::exception& e)
Um catch da mãe pega todas as filhas. E catch (...), com três pontos de verdade, pega qualquer coisa, até o que não é exceção da família (em C++ dá para lançar um int, embora você não deva):
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
void tarefa(int n) {
std::vector<int> v = {1, 2, 3};
if (n == 1) std::stoi("x");
if (n == 2) std::cout << v.at(5);
if (n == 3) throw std::runtime_error("o servidor não respondeu");
if (n == 4) throw 42;
}
int main() {
for (int n = 1; n <= 4; ++n) {
try {
tarefa(n);
} catch (const std::exception& e) {
std::cout << n << ": " << e.what() << '\n';
} catch (...) {
std::cout << n << ": erro desconhecido\n";
}
}
}
$ g++ -std=c++23 -Wall -Wextra geral.cpp -o geral $ ./geral 1: stoi 2: vector::_M_range_check: __n (which is 5) >= this->size() (which is 3) 3: o servidor não respondeu 4: erro desconhecido « o throw 42 não é std::exception: caiu no catch (...)
No catch (...) não existe variável e: você não sabe o que foi lançado, então não há what(). Use-o como última rede de segurança, por exemplo no main, para mostrar uma mensagem amigável em vez de abortar.
A ordem importa: do mais específico para o mais geral
Como vale o primeiro catch que serve, a mãe antes da filha "rouba" tudo:
#include <iostream>
#include <stdexcept>
int main() {
try {
throw std::out_of_range("posição 10");
} catch (const std::exception& e) {
std::cout << "geral: " << e.what() << '\n';
} catch (const std::out_of_range& e) {
std::cout << "específico: " << e.what() << '\n';
}
}
$ g++ -std=c++23 -Wall -Wextra ordem.cpp -o ordem
ordem.cpp: In function ‘int main()’:
ordem.cpp:9:7: warning: exception of type ‘std::out_of_range’ will be caught by earlier handler [-Wexceptions]
9 | } catch (const std::out_of_range& e) {
| ^~~~~
ordem.cpp:7:7: note: for type ‘std::exception’
7 | } catch (const std::exception& e) {
| ^~~~~
$ ./ordem
geral: posição 10 « o catch específico nunca roda
will be caught by earlier handler = "será pega por um tratador anterior". Ponha os catch das filhas primeiro e o da mãe (e o ...) por último.
6. A sua própria exceção
Num programa de banco, "saldo insuficiente" é um erro tão importante que merece um tipo próprio: assim, quem chama pode tratá-lo de um jeito especial e deixar os outros erros para um catch geral. O jeito mais simples de criar uma exceção é fazer uma filha de std::runtime_error:
#include <iostream>
#include <stdexcept>
#include <string>
class SaldoInsuficiente : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
struct Conta {
std::string dono;
double saldo = 0.0;
};
void sacar(Conta& c, double valor) {
if (valor <= 0) {
throw std::invalid_argument("valor do saque deve ser positivo");
}
if (valor > c.saldo) {
throw SaldoInsuficiente("saldo de " + c.dono + " não cobre o saque");
}
c.saldo -= valor;
}
int main() {
Conta ana{"Ana", 100.0};
for (double valor : {30.0, 500.0, -5.0, 70.0}) {
try {
sacar(ana, valor);
std::cout << "Sacou " << valor << ", saldo " << ana.saldo << '\n';
} catch (const SaldoInsuficiente& e) {
std::cout << "Recusado: " << e.what() << '\n';
} catch (const std::exception& e) {
std::cout << "Erro: " << e.what() << '\n';
}
}
}
$ g++ -std=c++23 -Wall -Wextra saldo.cpp -o saldo $ ./saldo Sacou 30, saldo 70 Recusado: saldo de Ana não cobre o saque Erro: valor do saque deve ser positivo Sacou 70, saldo 0
- class SaldoInsuficiente : public std::runtime_error
- Cria uma classe nova que herda tudo de
std::runtime_error: ela é umaruntime_error, e portanto também umastd::exception. Classes e herança são assunto do Nível 3 (Classes e Objetos, Herança); por enquanto, copie este modelo. - public: using std::runtime_error::runtime_error;
- "Use os construtores da mãe": é o que permite escrever
SaldoInsuficiente("mensagem"), igual astd::runtime_error("mensagem"). - throw SaldoInsuficiente("saldo de " + c.dono + " não cobre o saque");
- Lança como qualquer outra. A mensagem pode ser montada com
+. - catch (const SaldoInsuficiente& e)
- Trata só o saldo insuficiente. Os outros erros (como o
invalid_argumentdo valor negativo) caem nocatchgeral de baixo.
Exceção ou if?
Exceções não substituem o if. Use cada um no seu lugar:
| Situação | Use | Por quê |
|---|---|---|
| O problema é esperado e tratado ali mesmo (o usuário digitou uma opção inválida no menu) | if | Mais simples e mais rápido. Veja Validar a Entrada. |
A função não tem como cumprir o que prometeu, e quem sabe tratar está longe (dividir, sacar, stoi) | throw | A exceção leva o problema até quem sabe agir, e não pode ser ignorada sem querer. |
| Um "não achei" comum (procurar um nome na agenda) | Valor de retorno (bool, std::optional) | Não é um erro, é uma resposta possível. Veja optional e variant. |
Quando dá errado
O que aconteceu: uma exceção foi lançada e nenhum catch a pegou. O tipo vem entre aspas ('std::invalid_argument', 'std::out_of_range'…) e a mensagem, depois de what():.
Como resolver: descubra quem lança (o what() costuma dizer: stoi, vector::_M_range_check) e coloque a chamada dentro de um try com o catch do tipo mostrado. Para achar a linha exata, use o GDB.
O que aconteceu: o catch não tem &. A exceção é copiada e pode perder a mensagem.
Como resolver: catch (const std::exception& e).
O que aconteceu: um catch mais geral (a mãe) está antes de um mais específico (a filha), que nunca vai rodar.
Como resolver: inverta a ordem: filhas primeiro, mãe por último.
$ g++ -std=c++23 -Wall -Wextra seminclude2.cpp -o seminclude2
seminclude2.cpp: In function ‘int main()’:
seminclude2.cpp:2:16: error: ‘invalid_argument’ is not a member of ‘std’
2 | throw std::invalid_argument("teste");
| ^~~~~~~~~~~~~~~~
seminclude2.cpp:1:1: note: ‘std::invalid_argument’ is defined in header ‘<stdexcept>’; this is probably fixable by adding ‘#include <stdexcept>’
+++ |+#include <stdexcept>
1 | int main() {
O que aconteceu: faltou o #include <stdexcept>, e o g++ até diz qual (is defined in header ‘<stdexcept>’). Com #include <iostream> no arquivo, o mesmo código compila no g++ 15, porque o <iostream> traz o <stdexcept> "de carona". Não conte com isso: outro compilador, ou outra versão, pode não trazer.
Como resolver: inclua <stdexcept> sempre que usar as exceções padrão.
Referência rápida
| Escrita | Significado |
|---|---|
throw std::runtime_error("msg"); | Lança uma exceção; a função para ali. |
try { … } | Bloco vigiado. |
catch (const std::out_of_range& e) { … } | Trata um tipo específico (e as filhas dele). |
catch (const std::exception& e) { … } | Trata qualquer exceção padrão. |
catch (...) { … } | Trata qualquer coisa lançada (sem e). |
e.what() | A mensagem da exceção. |
class X : public std::runtime_error { public: using std::runtime_error::runtime_error; }; | Uma exceção própria. |
#include <stdexcept> | runtime_error, invalid_argument, out_of_range, logic_error… |
| Código de saída 134 | O programa foi abortado (por exemplo, exceção não tratada). |
Exercícios
1. Lançar
Complete para lançar um "argumento inválido" com a mensagem idade negativa:
2. Pegar qualquer exceção padrão
Complete o catch que pega qualquer filha de std::exception, do jeito recomendado, com o nome e:
3. A mensagem
Dentro do catch, qual chamada devolve a mensagem da exceção e?
4. O que mostra este trecho? try { std::cout << "A"; throw std::runtime_error("x"); std::cout << "B"; } catch (const std::exception& e) { std::cout << "C"; } std::cout << "D";
5. O programa terminou com terminate called after throwing an instance of 'std::out_of_range' e código 134. O que aconteceu?
6. Em que ordem devem vir os catch?
Missão final: estoque insuficiente
Missão: a lojinha está vendendo cadernos que não tem (rode e veja o estoque ficar negativo). Crie a classe EstoqueInsuficiente, filha de std::runtime_error, e faça vender lançá-la com a mensagem "só restam " + std::to_string(p.estoque) quando a quantidade pedida passar do estoque. A segunda venda deve mostrar Não deu: só restam 2. Se quiser, troque o catch para pegar só a sua exceção.
#include <iostream>
#include <stdexcept>
#include <string>
// crie aqui a classe EstoqueInsuficiente, filha de std::runtime_error
struct Produto {
std::string nome;
int estoque = 0;
};
void vender(Produto& p, int qtd) {
// se qtd for maior que p.estoque, lance EstoqueInsuficiente
p.estoque -= qtd;
}
int main() {
Produto caderno{"caderno", 5};
for (int qtd : {3, 10}) {
try {
vender(caderno, qtd);
std::cout << "Vendidos " << qtd << ", restam " << caderno.estoque << '\n';
} catch (const std::exception& e) {
std::cout << "Não deu: " << e.what() << '\n';
}
}
}
#include <iostream>
#include <stdexcept>
#include <string>
class EstoqueInsuficiente : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
struct Produto {
std::string nome;
int estoque = 0;
};
void vender(Produto& p, int qtd) {
if (qtd > p.estoque) {
throw EstoqueInsuficiente("só restam " + std::to_string(p.estoque));
}
p.estoque -= qtd;
}
int main() {
Produto caderno{"caderno", 5};
for (int qtd : {3, 10}) {
try {
vender(caderno, qtd);
std::cout << "Vendidos " << qtd << ", restam " << caderno.estoque << '\n';
} catch (const EstoqueInsuficiente& e) {
std::cout << "Não deu: " << e.what() << '\n';
}
}
}
Resumo
throwlança uma exceção: a função para e o erro "sobe" até umtry.try { … } catch (const Tipo& e) { … }trata o erro, e o programa continua depois docatch.- Sem
catch:terminate called after throwing an instance of …,what(): …, "Abortado" e código 134. - Família padrão (
<stdexcept>):std::exceptioné a mãe; useinvalid_argument,out_of_rangeeruntime_error. - Pegue sempre por
const&; leia a mensagem come.what(). - Vários
catch: das filhas para a mãe;catch (...)por último. - Exceção própria:
class X : public std::runtime_error { public: using std::runtime_error::runtime_error; };.