Pergunta

eu me deparei Este artigo escrito por Andrei Alexandrescu e Petru Marginean há muitos anos, que apresenta e discute uma classe de utilitário chamada ScopeGuard para escrever código seguro contra exceções.Eu gostaria de saber se a codificação com esses objetos realmente leva a um código melhor ou se ofusca o tratamento de erros, pois talvez o retorno de chamada do guarda seria melhor apresentado em um bloco catch?Alguém tem alguma experiência em usá-los em código de produção real?

Foi útil?

Solução

Definitivamente melhora seu código.Sua afirmação provisoriamente formulada de que é obscuro e que o código mereceria de um catch block simplesmente não é verdade em C++ porque RAII é um idioma estabelecido.Manipulação de recursos em C++ é feito por aquisição de recursos e coleta de lixo é feito por chamadas implícitas de destruidor.

Por outro lado, explícito catch os blocos aumentariam o código e introduziriam erros sutis porque o fluxo do código se torna muito mais complexo e o manuseio de recursos precisa ser feito explicitamente.

RAII (incluindo ScopeGuards) não é uma técnica obscura em C++, mas uma prática recomendada firmemente estabelecida.

Outras dicas

Sim.

Se há um único pedaço de código C++ que eu poderia recomendar a todo programador C++ que gaste 10 minutos aprendendo, é o ScopeGuard (agora parte do programa disponível gratuitamente Biblioteca Loki).

Decidi tentar usar uma versão (ligeiramente modificada) do ScopeGuard para um pequeno programa GUI Win32 em que estava trabalhando.O Win32, como você deve saber, possui muitos tipos diferentes de recursos que precisam ser fechados de maneiras diferentes (por exemplo,identificadores do kernel geralmente são fechados com CloseHandle(), GDI BeginPaint() precisa ser emparelhado com EndPaint(), etc.) Usei o ScopeGuard com todos esses recursos e também para alocar buffers de trabalho com new (por exemplo.para conversões de conjuntos de caracteres de/para Unicode).

O que me surpreendeu foi o quanto mais curta o programa era. Basicamente, é uma situação em que todos ganham:seu código fica mais curto e robusto ao mesmo tempo.Mudanças futuras no código não posso vazar nada.Eles simplesmente não podem.Quão legal é isso?

Costumo usá-lo para proteger o uso de memória, coisas que precisam ser liberadas e que foram retornadas do sistema operacional.Por exemplo:

DATA_BLOB blobIn, blobOut;
blobIn.pbData=const_cast<BYTE*>(data);
blobIn.cbData=length;

CryptUnprotectData(&blobIn, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &blobOut);
Guard guardBlob=guardFn(::LocalFree, blobOut.pbData);
// do stuff with blobOut.pbData

Não usei esse modelo específico, mas já usei algo semelhante antes.Sim, isso leva a um código mais claro quando comparado a códigos igualmente robustos implementados de diferentes maneiras.

Acho que as respostas acima carecem de uma observação importante.Como outros apontaram, você pode usar ScopeGuard para liberar recursos alocados independentemente de falhas (exceção).Mas essa pode não ser a única coisa para a qual você deseja usar o protetor de escopo.Na verdade, os exemplos no artigo vinculado usam ScopeGuard para um propósito diferente:transações.Resumindo, pode ser útil se você tiver vários objetos (mesmo que esses objetos usem RAII corretamente) que você precisa manter em um estado que esteja de alguma forma correlacionado.Se a mudança de estado de qualquer um desses objetos resultar em uma exceção (o que, presumo, geralmente significa que seu estado não mudou), todas as alterações já aplicadas precisarão ser revertidas.Isso cria seu próprio conjunto de problemas (e se uma reversão também falhar?).Você poderia tentar implementar sua própria classe que gerencia esses objetos correlacionados, mas à medida que o número deles aumentasse, ficaria confuso e você provavelmente voltaria a usar ScopeGuard internamente de qualquer maneira.

Sim.

Era tão importante em C++ que até mesmo uma sintaxe especial para ele em D:

void somefunction() {
    writeln("function enter");
    // c++ has similar constructs but not in syntax level
    scope(exit) writeln("function exit");

    // do what ever you do, you never miss the function exit output
}

Eu tenho que dizer, não, não, não importa.As respostas aqui ajudam a demonstrar por que é uma ideia genuinamente horrível.O manuseio de recursos deve ser feito por meio de classes reutilizáveis.A única coisa que eles conseguiram usando um protetor de escopo foi violar o DRY e duplicar o código de liberação de recursos em toda a base de código, em vez de escrever uma classe para lidar com o recurso e pronto, para todo o lote.

Se guardas de escopo têm algum uso real, o manuseio de recursos é não um deles.Nesse caso, eles são enormemente inferiores ao RAII simples, já que o RAII é desduplicado e os protetores automáticos e de escopo são duplicação ou falha manual de código.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top