Try-catch flutuante

Data: 2008-04-03
Categorias: C++

Esse detalhe da linguagem quem me fez descobrir foi o Yorick, que costuma comentar no blogue e tive o prazer de conhecer no 4o. EPA-CCPP.

É possível, apesar de bizarro, colocar um bloco try-catch em torno da lista de inicialização de variáveis de um construtor. Essa característica da linguagem permite que possamos capturar alguma exceção lançada por algum construtor de algum membro da classe. A construção em código ficaria no estilo abaixo:

Class::Class() try : initialization-list
{
	// Class constructor body
}
catch(...) // note: this IS right!
{
	// do something about
	// just like "throw" over here
}

Apesar dessa capacidade, não conseguimos parar o lançamento da exceção. Após seu lançamento, caímos no bloco catch abaixo do corpo do construtor e a exceção é lançada novamente, como se houvesse uma intrução throw no final do catch.

O exemplo abaixo demonstra um código de uma classe que captura a exceção durante a inicialização dos membros. Na seguida o catch da função main é executada, provando que a exceção de fato não é "salva" no primeiro bloco.

#include <iostream>
 
 
/* This class explodes */
class Explode
{
	public:
		Explode(int x) 
		{
			m_x = x;
			throw x;
		}
 
		void print()
		{
			std::cout << "The number: " << m_x << std::endl;
		}
 
	private:
		int m_x;
};
 
 
/* This class is going to be exploded */
class Victim
{
	public:
		Victim() try : m_explode(5)
		{
			std::cout << "You're supposed to NOT seeing this...\n";
		}
		catch(...) 
		{ 
			std::cerr << "Something BAD hapenned\n";
			std::cerr << "We're going to blow up\n";
			// just like 'throw' over here
		}
 
		void print()
		{
			m_explode.print();
		}
 
	private:
		Explode m_explode;
};
 
 
int main()
{
	try
	{
		Victim vic;
	}
	catch(...)
	{
		std::cerr << "Something BAD BAD happenned...\n";
	}
}
 

Testei esse código nos seguintes compiladores:

  • Visual Studio 6. Falhou, demonstrando desconhecer a sintaxe.
  • Borland C++ Builder 5. Falhou, demonstrando desconhecer a sintaxe.
  • Borland Developer Studio 4. Falhou, com o mesmo erro.
  • Visual Studio 2003. Comportamento esperado.
  • Visual Studio 2005. Comportamento esperado.
  • Visual Studio 2008. Comportamento esperado.
  • G++ (no Cygwin). Comportamento esperado.

A saída esperada é a seguinte:

Something BAD hapenned
We're going to blow up
Something BAD BAD happenned...

9 respostas para “Try-catch flutuante”

  1. Alberto Fabiano Diz:

    Meu caro,

    Só por curiosidade, eu também fiz o teste nos seguintes compiladores:

    -> ICC, Intel C++ Compiler 10.1: Comportamento OK
    -> DC, Digital Mars C++ Compiler 8.48: Comportamento OK
    []s

  2. Alberto Fabiano Diz:

    Pequena correção...

    O nome do compilador da digital Mars é DMC e não dc, porém quando eu percebi o erro eu já havia clicado no "submit comment" ;-(

    []s

  3. try catch no construtor | SKHAZ's Blog Diz:

    [...] Failures) lendo meus feeds vi que o Wanderley Caloni publicou primeiro sobre isso primeiro em Try-catch flutuante, o legal é que eu não sabia que nome poderia ser dado à [...]

  4. Thiago Adams Diz:

    Também pode ser usado assim:
    void f() try
    {
    throw 1;
    //throw "";
    }
    catch (int) {
    throw;
    }
    catch (...) {
    std::terminate();
    }
    que poderia simular algo assim:
    void f() throw(int)
    {
    }

  5. Wanderley Caloni Diz:

    Alberto: valeu pelos testes! É bom saber que existem outros compiladores tão bons quanto o GCC e o VC no mercado.

    Thiago: isso sim é bem esquisito! Te confesso que nem entendi direito o uso desse exemplo.

  6. Thiago R Adams Diz:

    Quando uma função possui a especificação das exceções significa que ela só pode lançar aquelas exceções. Caso seja lançado outra, o std::unexpected(); é chamado. O compilador VC++ não obedece essa especificação do C++. Mas é possível gerar o código na mão, usando aquela sintaxe com o try.
    Então por exemplo:

    void f() throw(int); //num compilador que obedece o padrao

    É equivalente a fazer "na mão":

    void f() try
    {
    //algo...
    }
    catch (int) {
    throw;
    }
    catch (...) {
    std::unexpected()
    }
    Este é outro uso curioso do try que eu resolvi comentar aqui.

  7. PopolonY2k Diz:

    Grande blog...

    ...Caloni, não o conhecia anteriormente e vejo que darei vários "pitacos" por aqui sempre que puder.

    Sobre o bloco try catch(...) em inicialização de variáveis e objetos em construtores, vale ressaltar o que o amigo Thiago Adams citou acima, que é o uso em qualquer tipo de função de C++, mas alguns cuidados devem ser tomados conforme reportado no padrão ANSI de C++ proposto em 1998.

    Sobre não ter funcionado em alguns compiladores reportados por você, como o Visual Studio 6 - VC++ 6.0, Borland C++ Builder 5 e o Borland Studio Developer Studio 4, deve deixo registrado aqui que a proposta de try catch na inicialização de funções veio na versão final da proposta do padrão ANSI para C++ em 1998, portanto compiladores, como o Visual C++ 6.0 (Visual Studio 6) e Borland C++ Builder 5 já estavam no mercado e portanto não existia maneira de que os mesmos respeitassem a nova sintaxe proposta pelo ANSI.

    Segue um artigo de referência no Dr. Dobbs.

    http://www.ddj.net/cpp/184401297

    Um abraço,

    PopolonY2k
    PlanetaMessenger.org

  8. Уθя¡ςκ Diz:

    Adoro esse livro do Danny Kalev por ele ser curto e grosso, o livro mais
    concentrado de C++ que já peguei, li ele a alguns anos mas realmente não
    me lembrava dessa passagem.

    Function try Blocks

    A function try block is a function whose body consists of a try block and its
    associated handlers. A function try block enables a handler to catch an
    exception that is thrown during the execution of the initializer expressions in
    the constructor's member initialization list or during the execution of the
    constructor's body. Note, however, that unlike handlers of ordinary exceptions,
    the handler of a function try block merely catches the exception -- it cannot
    continue the object's construction normally. This is because the partially
    constructed object is destroyed as a result of the stack unwinding. In addition,
    the handler of a function try block cannot execute a return statement
    (eventually, the handler must exit by a throw). What is the use of a function
    try block then? The handler enables you to throw a different exception than the
    one that it just caught, thereby preventing a violation of the exception
    specification.

    For example

    class X{};
    C::C(const std::string& s) throw (X) // allowed to throw X only
    try
    : str(s) // str's constructor might throw a bad_alloc exception,
    // might violate C's exception specification
    {
    // constructor function body
    }
    catch (...) //handle any exception thrown from ctor initializer or ctor body
    {
    //...
    throw X(); //replace bad_alloc exception with an exception of type X
    }

    In this example, a string object is first constructed as a member of class C.
    string might throw a bad_alloc exception during its construction. The function
    try block catches the bad_alloc exception and throws instead an exception of
    type X, which satisfies the exception specification of C's constructor.

  9. LorD FeniX Diz:

    Li muito rapidamente pois caí aqui por acaso no meio de uma pesquisa :)))

    mas, já tentou isso para evitar o relançamento da exceção?

    try{

    // insira seu erro :)))

    }catch(...){
    ShowMessage("ERRO!!!");
    Abort();
    }

Deixe uma resposta