quinta-feira, 29 de novembro de 2007

Artigo: Reinstall Windows and outfit your system with all freeware programs

Artigo do FreewareGenius bem interessante pra quem quer conhecer uma grande variedade de software de ótima qualidade e custo zero.

Acesse o artigo aqui e também leia os comentários, que por sinal tem links pra outros freewares interessantes.

sexta-feira, 23 de novembro de 2007

Dica C++: Minimizando a dependência de arquivos - Parte 1

Algumas vezes nos deparamos com situações em que as declarações que construímos precisam fazer referência umas às outras, de forma a implementar um relacionamento entre ambas as partes. Quando esse relacionamento é modelado, geralmente desejamos colocar cada parte declarada num arquivo diferente, de forma que fiquem logicamente e fisicamente separados.

Se por exemplo criamos duas classes, A e B, criamos dois arquivos de declaração respectivos, A.h e B.h. Só que nossa classe A precisa usar um objeto da classe B, que por sua vez também precisa usar um objeto da classe A. Nesse caso, se A.h incluir B.h e B.h incluir A.h, claramente está sendo criado uma dependência circular, o que não é permitido (A.h irá incluir a declaração de B.h, que irá incluir a declaração de A.h, que írá incluir...).

Na maioria dos casos, os programadores são levados a declarar as classes num mesmo arquivo, digamos AB.h. Veja o exemplo abaixo:

Por questões de simplificação, as classes foram criadas ignorando uso de constantes, operadores, construtores, destrutores, etc.

Arquivo AB.h_______________________________________________

#ifndef AB_H
#define AB_H
        class B; // pre-declaracao
        class A
{
public:
B* GetB();
void SetB(B *b);
private:
B *_b;
};
        class B
{
public:
A* GetA();
void SetA(A *a);
private:
A *_a;
};
#endif

Arquivo AB.cpp_____________________________________________

#include "AB.h"
// class A
B* A::GetB()
{
return ( _b );
}
void A::SetB(B *b)
{
_b = b;
}
// class B
A* B::GetA()
{
return ( _a );
}
void B::SetA(A *a)
{
_a = a;
}

__________________________________________________________


Isso termina com o problema da dependência circular, mas também traz alguns efeitos indesejados:



  • No arquivo de declaração é perdida a abstração de A separado de B e vice-versa;
  • Não há como reutilizar A sem B, e vice-versa (o principal);

Todavia, há outras formas de resolver ou minimizar o problema e conseqüentemente seus efeitos.


Solução 1: Limitar à dependência da declaração antecipada


Se A não tem conhecimento dos detalhes (métodos, atributos e subtipos) de B e vice-versa, eles só precisam saber que deve haver a respectiva declaração de quem eles precisam em algum lugar. Onde ? Não importa. Só precisam saber que existe. É aí que está a solução.


Isto quer dizer que A não precisa conhecer a declaração de B especificamente, mas apenas saber que B existe.


Arquivo A.h________________________________________________

#ifndef A_H
#define
A_H
        class B; // pre-declaracao
        class A
{
public:
B* GetB();
void SetB(B *b);
private:
B *_b;
}; 
#endif
__________________________________________________
(O arquivo A.cpp conterá a implentação de A mostrada no arquivo AB.cpp) 
 

Arquivo B.h________________________________________________

#ifndef B_H
#define B_H
        class A; // pre-declaracao
        class B
{
public:
A* GetA();
void SetA(A *a);
private:
A *_a;
}; 
#endif
__________________________________________________

(O arquivo B.cpp conterá a implentação de B mostrada no arquivo AB.cpp)


Em nenhum dos arquivos (de cabeçalho ou de implementação) é necessário especificar a declaração do tipo pré-declarado usado.


Assim, A e B tem um conhecimento praticamente abstrato um do outro. Cada um sabe que o outro existe, mas não sabe como ele é.


Com isso é aberta a possibilidade de reutilização de um mesmo arquivo em outros projetos, já que qualquer uma das partes pode variar que a outra continua não tendo conhecimento.


Vale lembrar que esta solução é válida pois as partes não se conhecem realmente (só se conhecem de nome). Quando precisarem se conhecer serão adotadas outras soluções, as quais veremos nas próximas partes deste modesto artigo. ;)

quinta-feira, 8 de novembro de 2007

Dica C++: Usando auto_ptr

A auto_ptr é uma classe parte da STL que permite guardar a referência para um objeto de qualquer tipo. Quando se cria um objeto da classe auto_ptr, passando para ela o objeto apontado, ele terá a posse do objeto apontado.

Por exemplo:

class ClasseQualquer
{
public:
int valor;
};

auto_ptr< ClasseQualquer > meuAutoPtr(
  new ClasseQualquer );


O objeto criado pela instrução new ClasseQualquer será agora o objeto apontado por meuAutoPtr.


Quando a instância de meuAutoPtr for destruída, ela destrói o objeto que ela aponta. Assim, quando o escopo da variável meuAutoPtr terminar, ela automaticamente irá liberar da memória o (dar um delete no) objeto para o qual ela aponta.


A classe auto_ptr possui o método get para retornar o objeto apontado:


meuAutoPtr.get(); // irá retornar a instância de ClasseQualquer


Logo, para alterar o atributo valor:


meuAutoPtr.get()->valor = 100;


Só que auto_ptr também sobrecarrega o operador ->, fazendo com que ele também retorne o objeto apontado (sendo X uma classe qualquer a assinatura do método seria "X* operator ->() const;". Devo falar mais sobre sobrecarga de operadores em postagens futuras.). Logo, podemos usar


meuAutoPtr->valor = 100;


como se meuAutoPtr fosse o próprio objeto de ClasseQualquer.


auto_ptr possui o método release, para liberar a memória do objeto apontado por ela:


meuAutoPtr.release(); // destroi a instância de ClasseQualquer


E o método reset, para fazer com que ela aponte para outro objeto. O método reset irá destruir o objeto apontado antes de receber o novo:


meuAutoPtr.reset( new ClasseQualquer );


Na grande maioria das vezes, não precisamos usar os métodos oferecidos por auto_ptr pois sua utilidade maior é mesmo a de poder guardar a referência para um objeto e destruí-lo automaticamente assim que seu escopo terminar.

void FuncaoExemplo()
{
// Um objeto de ClasseQualquer é
// instanciado

auto_ptr< ClasseQualquer > meuAutoPtr(
new ClasseQualquer );
 
// Podemos acessar o objeto criado atraves
// do objeto auto_ptr de forma praticamente
// transparente (devido ao operator ->)

meuAutoPtr->valor = 100;
 
cout << "O valor é: " << meuAutoPtr->valor;
 
// O MAIS IMPORTANTE:
// Nao e' preciso desalocar a memoria que
// foi criada e passada para o auto_ptr.
// Como o escopo da variavel meuAutoPtr
// terminou ela deleta o objeto apontado

}

Essa é uma maneira simples de criar objetos com new, sem que seja preciso usar delete (ou seja, sem que seja preciso se preocupar com a memória alocada).


Cuidados no uso de auto_ptr

Quando criamos um objeto da classe auto_ptr, ele geralmente não deve ser um ponteiro. Isso porque, sendo ponteiro, nos obrigaria a usar new e delete, o que faria perder o sentido da classe.
void FuncaoExemplo2_UsoSemSentido()
{
auto_ptr< ClasseQualquer > *meuAutoPtr =
new auto_ptr< ClasseQualquer >(
new ClasseQualquer );
 
( *meuAutoPtr )->valor = 100;
 
cout << "O valor é: " <<
( *meuAutoPtr )->valor;
 
delete meuAutoPtr;
}

Veja que foi preciso acessar o valor do ponteiro meuAutoPtr, usando asterisco...

( *meuAutoPtr )->valor = 100;

...para não precisar acessar usando get:

meuAutoPtr->get()->valor = 100;

Outro cuidado importante que deve-se ter é o atribuir uma variável da classe auto_ptr a outra. A atribuição passa a posse do objeto para a variável receptora (do lado esquerdo). Isto porque auto_ptr admite somente um dono para um objeto:

// Cria umDono possuindo uma instancia de
// ClasseQualquer

auto_ptr< ClasseQualquer > umDono(
new ClasseQualquer );
 
// Faz com que novoDono seja o novo dono da
// instancia e com que umDono não seja mais
// dono de nada !!!

auto_ptr< ClasseQualquer > novoDono = umDono;
 
umDono->valor = 100; // ERRO !
 
novoDono->valor = 100; // OK

Conclusão


Podemos usar a classe auto_ptr para nos ajudar a criar instâncias de outras classes sem que precisemos nos preocupar em gerenciar a memória alocada.


Em tempo, a classe pertence à biblioteca memory e é declarada dentro do namespace std.