Será que a memória são liberados quando eu lançar uma exceção?
-
19-09-2019 - |
Pergunta
Eu estava debatendo com alguns colegas sobre o que acontece quando você lançar uma exceção em uma classe alocada dinamicamente. Eu sei que malloc
é chamado, e em seguida, o construtor da classe. O construtor nunca retorna, então o que acontece com o malloc
?
Considere o seguinte exemplo:
class B
{
public:
B()
{
cout << "B::B()" << endl;
throw "B::exception";
}
~B()
{
cout << "B::~B()" << endl;
}
};
void main()
{
B *o = 0;
try
{
o = new B;
}
catch(const char *)
{
cout << "ouch!" << endl;
}
}
O que acontece com o o
memória malloced, isso vazar? O CRT capturar a exceção do construtor e desalocar a memória?
Felicidades!
Rica
Solução
Uma chamada para
new B();
resolve em duas coisas:
- alocando com um operador new () (ou o global ou uma classe específica, potencialmente uma colocação com o
new (xxx) B()
sintaxe) - chamar o construtor.
Se a reposição construtor, o operador correspondente exclusão é chamado. O caso em que a exclusão correspondente é uma colocação é o único caso em que um operador delete colocação é chamado sem a sintaxe :: operator delete (). delete x;
ou delete[] x;
não chamar os operadores Eliminar posicionamento e não há nenhuma sintaxe semelhante à colocação de novo para chamá-los.
Note que, enquanto o destruidor de B não ser chamado, já construídos subobjetos (membros ou B e classes de base de B) serão destruídas antes da chamada para operador excluir. O construtor que não é chamado é o único para B.
Outras dicas
Quando uma exceção é lançada a partir do construtor, a memória alocada pelo novo é liberado, mas o destruidor de classe B não é chamado.
Neste caso, o objeto, o, na verdade não se construiu, e a memória alocada pelo novo é liberado. Como tal, o destruidor não obter chamado. Então você não precisa chamar:
delete o;
Um padrão de design interessante é RAII - Resource aquisição é inicialização. Neste padrão, você usa um construtor para encapsular a aquisição de um recurso, e liberar o recurso no processo de destruição. Se o recurso não pode ser adquirido, você joga no construtor - muito parecido com o seu exemplo. Assim, se você tem um objeto válido, você tem o recurso.
Se o objeto é construído, então você ter adquirido o recurso com sucesso. Isto significa que para a vida do objeto, que possui o recurso. Quando o objeto é excluído, o recurso é liberado. Se o objeto não é construído, então você nunca adquiriu o recurso. Veja wikipedia:
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
A partir do C ++ 2003 Standard 5.3.4 / 17 - Novo:
Se qualquer parte da inicialização do objecto descrito acima termina por gerar uma exceção e uma função desatribuição adequado pode ser encontrado, a função desatribuição é chamado para libertar a memória em que o objecto foi sendo construído, após o qual a excepção continua a propagar no contexto da nova-expressão. Se nenhuma função deallocation correspondência inequívoca pode ser encontrado, propagar a exceção não causa a memória do objeto a ser libertado. [Nota: Isto é apropriado quando a chamada função de alocação não aloca memória; caso contrário, é provável que resulte em uma fuga de memória. ]
Portanto, não pode ou não ser um vazamento - que depende se um desalocador apropriado pode ser encontrado (que é normalmente o caso, a menos que operator new / delete ter sido substituído) .No caso em que há uma desalocador adequado, o compilador é responsável por fiação em uma chamada para que se o construtor lança.
Note que este é mais ou menos alheios ao que acontece com recursos adquiridos no construtor, que é o que a minha primeira tentativa de uma resposta discutido -. E é uma questão que é discutida em muitos FAQs, artigos e postagens