Tag Archives: programação

Livro: programação shell linux

Acabei de ler o livro: Programação Shell linux, do vovô do shell script. Eu acabei de ver a 5ª edição, só para esclarecer :p.

Bom, só tenho a dizer que o filme é bem legal. Explica tudo sobre shell script, e ainda de forma bem legal. Veja que eu li a 5ª edição, e já esta na 7ª edição ;).

Explica tudo mesmo, do básico, como variáveis, condicionais e comandos básicos, até coisas muito mais avançadas, como parâmetros, pipes e programas mais avançados.

Só tenho a dizer que gostei muito do livro, e recomendo. Posso dizer que o livro, junto com o livro do Aurélio(Esse eu vou falar depois…), é um tratado final sobre como programar em shell script.

 

Bom, fica ai a dica. Recomendo, e quem quiser eu tenho ;).


Aquisição e desalocação de recursos com objetos (Um pouco de RAII)

O que é a RAII?

Falar de RAII é ser sempre um pouco obvio, mas os conceitos que vem com ela de vez em quando não estão “fixos”. Sabendo o que é a técnica, o que ela faz e como implementar, podemos usa-la de forma melhor.

A RAII é um padrão de projeto que especifica que todos os recursos de um objeto devem ser adquiridos/alocados quando o objeto for inicializado.

E também, todos os recursos que um objeto tem que são exclusivos dele, devem ser liberados/desalocados no destrutor do objeto.

Fazendo isso, você tem um objeto que respeita o RAII.

Por que usar o RAII?

Bom, existem vários motivos para se usar o RAII. O mais convincente deles talvez seja que o C++(assim como a linguagem D), foi projetada para esse designe. Isso porque o C++ assume que o que você cria no objeto só vai existir no escopo daquele objeto.

Isso seria como o escopo de funções, você aloca/adquiri recursos no começo da sua função, como no exemplo abaixo:

/* exemplo de como seria o escopo */
void foo( int &x ){

	vector<double> tempArray;
	int *aux = new int ;

/* Aqui continua a sua função */
. . . .

No começo da função “foo”, você adquiriu o recurso “tempArray” e alocou um inteiro “aux“.

Isso seria justamente o que você iria fazer no construtor do objeto, e que deve ser o inicio do escopo de um objeto, comparado ao escopo de uma função.

A primeira coisa que é executado no objeto, pouco depois de adquirir recursos declarados será o construtor do objeto. Então, seria uma classe assim que seria a equivalente da função “foo”:

class foo {
/**Aqui você "adquiri" um recurso quando o objeto começa a existir.
 * Esse recurso deve ser deslocado automaticamente e executado seu destrutor
 * quando o objeto deixar de existir, porque saiu do "escopo".
 */
	vector<double> tempArray;
	int *aux;
/* Agora o construtor. Aqui você "aloca" os seus recursos dinâmicos.
 * Aqui é preferível que você também defina atributos do objeto.
 */
	foo( int &x ) {
		this->aux = new int ;
/* Aqui continua a sua classe */
. . . . .

Esse é o inicio do seu objeto, mais o RAII ainda não esta completo. O RAII também deve abranger o destrutor, porque é ele que vai “desalocar/liberar” os recursos que o seu objeto.

Quando é requisitado que o objeto deixe de existir, o destrutor vai ser executado e depois será destruído os atributos do objeto, Isso porque o destrutor, comparado com uma função, representa o final do código que está na função, antes da função acabar e sair de escopo. Por exemplo, esse é o final da função “foo” que foi mostrada no começo:

	/*Final da função foo, final do escopo da função.
	* Será desalocado todos os recursos dinâmicos alocados nessa
	* Função.
	*/
	delete aux ;
	/*Ainda temos o objeto tempArray que pertence a esse escopo. Mas,
	* como já é o final do escopo, esse recurso terá chamado seu destrutor
	* e será desalocado automaticamente.
	*/
}

Esse código seria o final da função foo, que esta nos ajudando a entender o como funciona o escopo de um objeto.

Esse código equivale ao que a esse destrutor do objeto:

~foo(){
	/*O destrutor de foo foi chamado. Isso significa que o objeto vai
	* deixar de existir, então será desalocado todos os recursos dinâmicos
	* que pertenção exclusivamente a esse objetos. No caso desse objeto,
	* this->aux contem um recurso que alocamos no construtor, então ele
	* precisa ser deletado:
	*/
	delete this->aux ;
	/*Ainda temos o objeto tempArray que pertence ao escopo do nosso objeto.
	* Mas, como já é o final do escopo, esse recurso terá chamado seu destrutor
	* e será desalocado automaticamente.
	*/
}

Bom, pelo que da para ver com esse construtor e esse destrutor, o objeto terá seus recursos adquiridos no construtor e desalocados no destrutor.

Vejam que isso se adequá de forma perfeita em “C++“. Parece até redundância ficar falando isso, mas não é assim em algumas linguagens e algumas pessoas não usam esse conceito direito.

Código Exception Safe

Também é possível escrever código “exception safe“, ou seja, código que seja seguro para o uso de excessões.

Isso significa que você poderá usar excessões em qualquer parte do código, de forma que o seu programa gerado não tenha nenhum problema de vazamento de memória ou consistência por causa de uma excessão gerada em alguma parte de seu código.

Você garante isso porque você defini muito bem o comportamento de um objeto. Se você consegue definir as ações do destrutor e do construtor bem, você garante que fora do escopo tudo daquele objeto vai ser destruído quando ele for finalizado. Então não importa se você chegou no final da sua rotina, o objeto vai ser eliminado assim que aquele escopo deixar de existir. Isso garante o “exception safe“.

(Caso você não saiba muito sobre exceções e “exception safe“, clique em “exception safe” no link no título desse tópico e veja o artigo Bjarne, o criador.)

Por que Não usar o RAII?

Não existe motivos para não usar RAII, existem motivos para não usar RAII completamente.

Então, porque não usar RAII completamente? Simplesmente porque, em alguns casos, os recursos que o objeto adquiri para si são muito grandes, e talvez seja mais vantajoso iniciar o objeto com uma parte do recurso ou até mesmo adiar a aquisição do recurso.

Isso ocorre por exemplo quando o objeto precisa de um vetor muito grande ou quando você não sabe se o recurso será realmente necessário ou não. Então, como uma estratégia para otimizar os recursos que seu objeto vai consumir, você opta por uma inicialização tardia ou sob-demanda.

Bons exemplos sobre isso seriam a classe da biblioteca padrão vector e list. As duas são exemplos dos dois casos.

Mas, independente da construção do objeto, a destruição ainda tem que ter o mesmo modo de agir. O único problema é que deve se checar no destrutor se o recurso já foi alocado e depois libera-lo ou não. Caso você faça uso de algum tipo de “smart pointer” (um dia falo mais sobre eles . . .) você não ira precisar nem verificar se o recurso foi alocado, ficam isso ao encargo do “smart pointer”.

Considerações finais

Creio que passei bem o pensamento por traz desse assunto. Falar sobre RAII é, de certa forma, falar do obvio. Mas no fundo todo o design pattern é falar um pouco sobre o obvio, só que o obvio que a gente nunca usa….

Também estou criando coragem. Hoje estou escrevendo sobre RAII, mas daqui a pouco vou escrever sobre “smart pointers“. Esse sim será um assunto BEM mais interessante. Mas por enquanto, fica só como projeto do futuro.

E como final, se lembrem sempre:

  • Construtor => Você deve garantir que o objeto tenha seus atributos definidos e que todos os recursos sejam adquiridos/alocados.
  • Destrutor => Você deve garantir que todos os recursos que pertencem ao objeto sejam desalocados/liberados.

Esse sempre será o comportamento desejável, previsível e esperado de uma classe bem feita.


%d bloggers like this: