Eu estava casualmente fazendo alguns desafios no HackerRank e me deparei com "Código Herdado", que tentei resolver com isso:
class BadLengthException : public exception {
private:
const char* message;
public:
BadLengthException(int n_) {
this->message = to_string(n_).c_str();
}
const char * what() const throw() {
return this->message;
}
};
Como não funcionou (a saída estava vazia), pesquisei no SO o motivo e encontrei esta pergunta sobre o mesmo problema. Acho que entendo a solução proposta, mas ainda estou muito confuso sobre minha tentativa:
Entendo que a string to_string(n_).c_str()
pode ser vista como uma variável local, como no exemplo da pergunta mencionada, mas por que ainda é tratada como tal quando what()
é chamada? Não deveria ter sido mantido message
via inicialização, estando disponível sempre que chamar what()
?
const char*
é um ponteiro para uma string C bruta. Não é um objeto contêiner (comostd::string
) que possuiria a string e gerenciaria a alocação/desalocação do buffer que contém a string.to_string(int)
retorna umstd::string
com uma representação de string do número. Em seguidac_str()
, retorna um ponteiro para essa string.Mas em
this->message = to_string(n_).c_str();
, primeiro umstd::string
objeto temporário é construído e, em seguida,message
é definido como o ponteiro para essa string. Mas depois, o temporáriostd::string
é excluído (o que desaloca seu buffer) emessage
agora é um ponteiro pendente, que aponta para a memória que não está mais alocada.Uma solução seria, em vez disso, criar
this->message
um arquivostd::string
. E façawhat()
o retornothis->message.c_str()
.to_string(n_)
é temporário . Ele expira no final da expressão.c_str()
obtém um ponteiro para o buffer de dados da string temporária no curto período de tempo em que ostring
está ativo, e esse ponteiro é armazenado, deixando o programa com um ponteiro pendurado quase instantaneamente. Você está preso segurando ostring
, eu estou com medo.Uma possível solução:
Nota: É um risco alocar armazenamento em uma exceção, as coisas ficam muito ruins se esse armazenamento não puder ser alocado e você acabar com um lançamento após o lançamento, mas você pode mitigar isso com uma matriz pré-alocada e evitar o uso indo
std::string
para a velha escolasprintf
ou similar.message
é um ponteiro pendurado. O temporáriostd::string
retornado porstd::to_string
é destruído no final da expressão completa.Você precisa armazenar a mensagem em algum lugar. O melhor lugar é provavelmente em um arquivo
std::string
.Exemplo:
Observe que
const throw()
é a versão anterior ao C++11. Desde C++ 11, deve serconst noexcept override
.Uma opção mais simples é herdar de
std::runtime_error
, e é por isso que as pessoas costumam fazer isso ao criar exceções personalizadas. Ele fornece armazenamento, bem como umawhat()
substituição, então tudo que você precisa fazer é: