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.
Informação e tecnologia
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.
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:
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. ;)
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.
A 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.
Copyright 2009 thiago.WebLog. Powered by Blogger.