O que é RAII? Exemplos?


19

Sempre que o termo RAII é usado, as pessoas estão realmente falando sobre desconstrução, em vez de inicialização. Acho que tenho um entendimento básico do que isso pode significar, mas não tenho muita certeza. Além disso: C ++ é a única linguagem RAII? E o Java ou C # / .NET?

Respostas:


25

Aquisição de recursos é inicialização significa que os objetos devem cuidar de si mesmos como um pacote completo e não esperar que outro código diga uma instância "e, a propósito, você será limpo em breve - por favor, arrume agora". Geralmente, significa que há algo significativo no destruidor. Isso também significa que você escreve uma classe especificamente para gerenciar recursos, sabendo que sob certas circunstâncias difíceis de prever, como exceções sendo lançadas, você pode contar com a execução de destruidores.

Digamos que você queira escrever algum código no qual altere o cursor do Windows para um cursor de espera (ampulheta, rosquinha-de-não-trabalhar, etc.), faça suas coisas e depois altere-o novamente. E diga também que "faça suas coisas" pode lançar uma exceção. A maneira da RAII fazer isso seria criar uma classe cujo ctor configurou o cursor para aguardar, cujo único método "real" fez o que você queria que fosse feito e cujo dtor configurou o cursor de volta. Os recursos (neste caso, o estado do cursor) estão vinculados ao escopo de um objeto. Ao adquirir o recurso, você inicializa um objeto. Você pode contar com a destruição do objeto se houver exceções, o que significa que você pode contar com a limpeza do recurso.

Usar bem o RAII significa que você não precisa finally. Obviamente, ele depende da destruição determinística, que você não pode ter em Java. Você pode obter uma espécie de destruição determinística no C # e no VB.NET com using.


4
Eu acho que é isso que você está falando, mas você pode querer adicionar que a razão pela qual Java e C # não suportam RAII é por causa do coletor de lixo. No C ++, um objeto local será destruído assim que sair do escopo. Em Java / C # isso não é verdade.
Jason Baker

Expandindo o ponto Jasons, a razão pela qual Java e C # não podem garantir a destruição oportuna é devido à possibilidade de ciclos de referência, o que significa que é impossível determinar uma ordem segura para executar os destruidores. Ciclos de referência também podem acontecer em C ++, mas as implicações são diferentes - o programador se torna responsável por determinar a ordem de destruição e fazer exclusões explícitas. Essa responsabilidade é muitas vezes empacotada em algum destruidor de classes de nível superior - por exemplo, uma classe de contêiner é responsável por garantir que todos os itens contidos sejam destruídos. "Propriedade" é a chave.
Steve314

1
@ Jason, é o que eu quis dizer com "destruição determinística" - um programador de C ++ sabe quando o destruidor será executado.
Kate Gregory

Sei que essa é uma resposta antiga, mas ainda estou um pouco confusa. Acabei de conhecer o termo e algumas informações dizem que a aquisição deve acontecer no construtor. Isso realmente não faz sentido para mim e esta resposta parece contradizê-la, mas você poderia esclarecer?
Per Johansson

1
@PerJohansson Sim, você adquire no ctor. E você solta no dtor. Eu estava focando no segundo ponto, mas eles vão juntos. Depois que o ctor terminar, você saberá que possui um objeto válido. E você sabe que não importa o que aconteça, o recurso será lançado no momento certo.
5606 Kate Gregory

4

O RAII trata parcialmente de decidir quando um objeto se torna responsável por sua própria limpeza - a regra é que o objeto se torna responsável se e quando a inicialização do construtor for concluída. A simetria de inicialização e limpeza, construtor e destruidor, significa que os dois têm laços estreitos entre si.

Um ponto do RAII é garantir a segurança das exceções - que o aplicativo permaneça autoconsistente quando as exceções forem lançadas. À primeira vista, isso é trivial - quando uma exceção faz com que um escopo saia, as variáveis ​​locais nesse escopo precisam ser destruídas.

Mas o que acontece se o lançamento da exceção ocorrer dentro de um construtor?

Bem, o objeto não foi totalmente construído, portanto não pode ser destruído com segurança. O construtor deve ter blocos de tentativa, conforme necessário, para garantir que todas as limpezas necessárias sejam feitas antes que a exceção seja propagada. Depois que a exceção se propagar fora do escopo em que o objeto foi construído, não haverá chamada de destruidor, porque o objeto não foi construído em primeiro lugar.

Considere, em particular, os construtores para dados de membros dentro do objeto que estão sendo destruídos. Se um deles lançar uma exceção, seu código principal do construtor não será executado - mas algum código que forme uma parte implícita desse construtor terá. Quaisquer membros que foram construídos com sucesso serão automaticamente destruídos. Quaisquer membros que não foram construídos (incluindo o que lançou a exceção) não são.

Então, basicamente, a RAII é uma política que garante que tudo que for totalmente construído será destruído em tempo hábil, principalmente na presença de lançamentos de exceção, e que qualquer objeto seja totalmente construído ou não é (não há meio- objetos construídos que você não sabe como limpar com segurança). Os recursos alocados também são liberados. E muito do trabalho é automatizado, para que o programador não precise se preocupar muito com isso.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.