Boost Scoped Pointer (scoped_ptr)

Julho 24, 2008

Para ver o artigo anterior da série: Boost Weak Pointer

No post anterior prometi falar sobre arrays, mas resolvi deixar para o próximo post, pois dessa forma já posso incluir o scoped_array.

O Boost Scoped Pointer é uma versão bem simplificada de um smart pointer, que como o nome já diz depende do escopo. Ele é semelhante ao auto_ptr: quando seu destrutor é rodado, o ponteiro que este contém é deletado. A diferença é que o scoped_ptr não pode ser copiado e nem tem um método release (que libera o ponteiro). Ou seja, uma vez inicializado o scoped_ptr, ele se torna dono do ponteiro e vai deletar ele no final da sua vida.

Se o scoped_ptr não pode ser copiado, nem feito release, não pode ser usado com STL (essa não tinha dito ainda), então para que usar ele? Ele é útil quando precisamos alocar um objeto dinamicamente dentro de outro objeto ou quando precisamos de algo dinâmico dentro de algum escopo.

Por exemplo:


#include <boost/scoped_ptr.hpp>

int main(int, char **argv)
{
	boost::scoped_ptr<Person> p(new Person());

	p->SetName("Bruno");

	return(0);
}

Basicamente é o mesmo exemplo dos programas anteriores, a diferença é que o scoped_ptr, diferentemente do shared_ptr não precisa alocar um outro objeto para gerenciar contagem de referencias, dessa forma ele permite que qualquer compilador descente, consiga pegar o código acima e gerar:


int main(int, char **argv)
{
	Person *p(new Person);

	p->SetName("Bruno");

	delete p;

	return(0);
}

Ou seja, o scoped_ptr não tem custo algum em tempo de execução! Claro que o exemplo acima é bem simples, e o ideal seria criar a classe Person na pilha. Vamos tentar outro exemplo…

Imaginemos que temos um programa de processamento de imagens, e este programa possui uma classe para realizar operações com uma imagem, como imagens geralmente consomem muitos recursos, não é desejável que a classe de operações possua uma imagem como atributo, e como a imagem não vai ser compartilhada, podemos usar uma scoped_ptr:


#include <boost/scoped_ptr.hpp>
#include <string>

class Image
{
	public:
		int Load(const std::string &name)
		{
			//implementacao

			return 0;
		}

};

class ImageOperation
{
	public:
		int Load(const std::string &name)
		{
			m_image.reset(new Image());

            return(m_image->Load(name));
        }

    private:
        boost::scoped_ptr<Image> m_image;

};

int main(int argc, char **argv)
{
    ImageOperation op;

    op.Load("teste.bmp");

    return 0;
}

Nesse exemplo simples vimos que usando um scoped_ptr não precisamos nos preocupar mais com gerenciamento de ponteiros, pois o scoped_ptr garante que todo ponteiro armazenado por ele vai ser desalocado. Esta é uma dos smart pointers mais fáceis e práticos de se usar, inclusive para quem viu o Google C++ Style Guide, ele esta na lista de recomendações. 

No próximo post, vamos ver finalmente como lidar com arrays (o scoped_ptr também não serve para arrays).


Boost Weak Pointer (weak_ptr)

Julho 17, 2008

Para ver o artigo anterior da série: Boost Shared Pointer

No ultimo post vimos como funciona o shared_ptr da Boost e o problema da referência circular. Pensando em como resolver esse problema foi então criado o weak_ptr (ponteiro fraco), que também é um smart pointer, mas possui algumas restrições de uso:

  • Um weak_ptr só pode ser criado a partir de um shared_pointer
  • O ponteiro armazenado pelo weak_ptr somente pode ser usado após a chamada do método lock.

Voltando ao exemplo anterior, vamos modificar a classe um pouco:


#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <string>

class Person;

class Person
{
    public:
        typedef boost::shared_ptr<Person> PersonPtr_t;
        typedef boost::weak_ptr<Person> PersonWeakPtr_t;

        inline void SetName(const std::string &name)
        {
            m_Name = name;
        }

        inline void SetFilho(PersonPtr_t ptr)
        {
            m_Filho = ptr;
        } 

        inline void SetPai(PersonPtr_t ptr)
        {
            m_Pai = ptr;
        }

    private:
        std::string m_Name;

        PersonPtr_t m_Filho;
        PersonWeakPtr_t m_Pai;
};

Agora a classe Person armazena um weak_ptr para o pai ao invés de um shared_ptr. A decisão sobre quem vai ser weak e quem vai ser shared depende do projeto em si. No exemplo acima, tanto faz, escolhi o pai pensando que o certo seria o pai armazenar uma lista de filhos (que não implementei para deixar o exemplo simples) e a estrutura acabaria ficando parecido com uma arvore, e geralmente em arvores o que importa mesmo é a lista de filhos de cada nó, sendo a referencia para o pai apenas uma conveniência.

Mas voltando a weak_ptr, note que o método SetPai recebe como parâmetro um shared_ptr, não um weak_ptr. Implementamos assim pois um weak_ptr deve ser sempre inicializado a partir de um shared_ptr ou um outro weak_ptr. Se for o caso, pode ser adicionado uma sobrecarga com weak_ptr também.

Utilizando a nova classe:


int main(int argc, char **argv)
{
    Person::PersonPtr_t pai(new Person());
    pai->SetName("Pai");

    Person::PersonPtr_t filho(new Person());
    filho->SetName("Filho");
    filho->SetPai(pai);

    pai->SetFilho(filho);

    return 0;
}

Sim! Não mudou nada em relação ao exemplo do post anterior. Para deixar mais interessante, vamos criar um novo método e usá-lo:


#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <string>
#include <iostream>

class Person;

class Person
{
    public:
        typedef boost::shared_ptr<Person> PersonPtr_t;
        typedef boost::weak_ptr<Person> PersonWeakPtr_t;

        inline void SetName(const std::string &name)
        {
            m_Name = name;
        }

        inline void SetFilho(PersonPtr_t ptr)
        {
            m_Filho = ptr;
        } 

        inline void SetPai(PersonPtr_t ptr)
        {
            m_Pai = ptr;
        }

        inline const std::string &GetName() const
        {
            return(m_Name);
        }

        inline const std::string *GetNomePai()
        {
            PersonPtr_t ptr = m_Pai.lock();

            return(ptr ? &ptr->GetName() : NULL); 

        }

    private:
        std::string m_Name;

        PersonPtr_t m_Filho;
        PersonWeakPtr_t m_Pai;
};

int main(int argc, char **argv)
{
    using namespace std;

    Person::PersonPtr_t pai(new Person());
    pai->SetName("Pai");

    Person::PersonPtr_t filho(new Person());
    filho->SetName("Filho");
    filho->SetPai(pai);

    pai->SetFilho(filho);

    const std::string *name = filho->GetNomePai();
    cout << "Nome do pai: " << (name ? *name : "sou orfao&quot ;) << endl;

    return 0;
}

Pronto, agora já sabemos como usar o weak_ptr. Note que no método GetNomePai verificamos se o ponteiro ptr é valido antes de usá-lo. Isto deve ser feito pois um weak_ptr não garante que um objeto vai ser valido, apenas garante que uma referencia é valida ou null.

Isso quer dizer que se fizermos:


int main(int argc, char **argv)
{
    using namespace std;

    Person::PersonPtr_t pai(new Person());
    pai->SetName("Pai");

    Person::PersonPtr_t filho(new Person());
    filho->SetName("Filho");
    filho->SetPai(pai);

    pai->SetFilho(filho);
    pai.reset(); //apagando a referencia

    const std::string *name = filho->GetNomePai();
    cout << "Nome do pai: " << (name ? *name : "sou orfao&quot ;) << endl;

    return 0;
}

Neste novo exemplo, ao invés do nome do pai, vai ser impresso a mensagem “sou órfão”, porque quando o reset do ponteiro do pai é invocado, o objeto pai é destruído. Nesse caso, o weak_ptr nos retorna null. Se ao invés do weak_ptr estivéssemos usando um ponteiro comum, acessaríamos um ponteiro invalido.

No próximo post, vamos ver como usar smart pointers com vetores (arrays).

Próximo artigo da série: Boost Scoped Pointer


Boost Shared Pointer (shared_ptr)

Julho 9, 2008

Para ver o artigo anterior da série: Auto Pointer (auto_ptr)

Finalmente vamos ver um verdadeiro smart pointer :). O boost shared pointer é um smart pointer que gerencia a vida dos objetos utilizando contagem de referencias.

A contagem de referencias nada mais é do que um contador associado a cada objeto. Toda vez que um smart pointer passa a apontar para um objeto, ele incrementa esse contador, quando o smart pointer é destruído ou passa a apontar para outro objeto, o contador é decrementado. E por fim, o smart pointer toda vez que decrementar o contador deve verificar o seu valor, se chegou a zero, então ninguém mais referencia aquele objeto, logo ele pode ser destruído.

Vamos ao primeiro exemplo:


#include <boost/shared_ptr.hpp>

int main(int argc, char **argv)
{
    using namespace boost;
    shared_ptr<int> p(new int(5));    //contador de referencias agora é 1

    *p = 3;    //alterando o valor do inteiro

    shared_ptr<int> p2 = p;    //contador de referencias é 2, p e p2 apontam para o mesmo objeto

    return 0;        

    //p2 e p sao destruidos, contador de referencias chega a zero e int tambem é destruido.
}

Simples não? Agora vamos criar um pequeno objeto e usa-lo com o shared pointer:


#include <boost/shared_ptr.hpp>
#include <string>

class Person
{
    public:
        inline void SetName(const std::string &name)
        {
            m_Name = name;
        }

    private:
        std::string m_Name;
};

int main(int argc, char **argv)
{
    using namespace boost;

    shared_ptr<Person> p(new Person());

    p->SetName("Bruno");

    return 0;
}

Note que não existem diferença nenhuma de uso em relação a um ponteiro normal (assim como os outros smart pointer que vimos anteriormente). Agora vamos fazer uma pequena mudança na classe pessoa:


#include <boost/shared_ptr.hpp>
#include <string>

class Person;

class Person
{
    public:
        typedef boost::shared_ptr<Person> PersonPtr_t; 

        inline void SetName(const std::string &name)
        {
            m_Name = name;
        }

        inline void SetFilho(PersonPtr_t ptr)
        {
            m_Filho = ptr;
        } 

        inline void SetPai(PersonPtr_t ptr)
        {
            m_Pai = ptr;
        }

    private:
        std::string m_Name;

        PersonPtr_t m_Filho;
        PersonPtr_t m_Pai;
};

int main(int argc, char **argv)
{
    Person::PersonPtr_t pai(new Person());
    pai->SetName("Pai");

    Person::PersonPtr_t filho(new Person());
    filho->SetName("Filho");

    pai->SetFilho(filho);

    return 0;
}

Agora criamos dois objetos dinamicamente e ainda associamos um com o outro (pai passou a possuir uma referencia para Filho). No final da execução da função main, o primeiro a ser destruído é o ponteiro filho (lembre-se, em C++ a destruição de variáveis locais é sempre feita na ordem inversa de criação). O ponteiro filho vai decrementar o contador de referencias que vai chegar a um, pois o objeto “Pai” possui também um ponteiro para Filho. Em seguida, o ponteiro pai vai ser destruído, como seu contador de referencias chega a zero, ele destrói o objeto “Pai”, em consequência o destrutor de “Pai” é executado, forçando a destruição das suas variáveis membros, o que significa que o membro “m_Filho” é destruído, nesse momento o contador de referencias do filho finalmente chega a zero e ele é então destruído!.

A sequência de destruição fica parecida com:


filho.~shared_ptr()
DecrementaContador();

pai.~shared_ptr()
DecrementaContador();
Destroy()
Person::~Person()
m_Filho::~shared_ptr()
DecrementaContador()
Destroy()
Person::~Person() //Destrutor do filho é executado

Note que ainda não usei o método SetPai, que vai ser usado no próximo exemplo.

Referências Circulares

No exemplo anterior não utilizei o método SetPai porque ele causa um problema fatal (que vai nos gerar um memory leak):


int main(int argc, char **argv)
{
    Person::PersonPtr_t pai(new Person());
    pai->SetName("Pai");

    Person::PersonPtr_t filho(new Person());
    filho->SetName("Filho");

    pai->SetFilho(filho);    //contador de filho chega a 2
    filho->SetPai(pai);        //contador de pai chega a 2

    return 0;
}

No final do bloco, quando o ponteiro filho é destruído, ele decrementa o contador de referências do filho, que chega a um, nesse caso, o objeto filho não é destruído (lembre-se, o objeto pai também possui uma referência). Em seguida, o ponteiro pai é destruído, ele vai então decrementar o contador de referencia do pai, que também chega a um, logo ele também não é destruído!!!

Resultado: nosso programa não possuí mais nenhuma referência para os objetos, mas como um referência o outro, a contagem de referencia nunca chega a zero, logo eles nunca são destruídos!! É como aquele casal de namorados chatos no telefone:

Ela: Desliga amor
Ele: Não desliga você mor!
Ela: Só desligo depois de você amor!
Ele: Eu também só vou desligar depois de você mor!

Overhead

O shared pointer possui um pequeno overhead toda vez que ele é copiado (pois tem que ficar administrando o contador de referencias). Mas o maior overhead, que pode pesar para algumas aplicações (como jogos em consoles) é a própria criação do contador de referencias.

Toda vez que criamos um novo objeto e associamos ele a um shared_ptr, um objeto para controle de referencias é criado (que é bem pequeno, deve ser do tamanho de um ou dois ponteiros). Mas temos o custo da alocação de memória extra e da possível fragmentação (não sei se a boost internamente usa algum pool para minimizar isso). Mas para a maioria das aplicações, isso é irrelevante.

Na pagina da boost existe uma parte da documentação que mostra o overhead: Smart Pointer Timings.
! Conclusões

Os smart_pointers da boost são excelentes e resolvem muitos dos problemas que enfrentamos no dia dia, eu recomendo muito o seu uso, é muito melhor do que fazer o seu próprio, pois o shared_ptr resolve vários problemas que não tratamos aqui:

  • Exception safe
  • Multithread: é possível que varias threads compartilhem o mesmo objeto, não o mesmo shared_ptr. E isso não torna o seu objeto thread safe, esse problema é do programador!
  • Casts (que vamos ver no futuro)
  • Já foi testado, testado, usado, consertado, …, etc

No próximo post vamos explorar o problema da referencia circular e conhecer mais um smart pointer da boost.

Próximo artigo da série: Boost Weak Pointer


Google C++ Testing Framework

Julho 4, 2008

Esse blog ta começando a virar canal de noticias, não é a minha idéia, mas como ando sem tempo nas ultimas semanas, aqui vai outra dica bacana (valeu Zé).

O Google liberou hoje um framework para testes C++, dei uma olhada rápida e parece interessante, pelo menos no guia que eles disponibilizaram deu para ver que não tem muita frescura, basta usar alguns macros e sair testando tudo :).

Ai vão os Links:

Em breve vou estar postando a continuação da serie sobre smart pointers!


Entrevista com o Bjarne

Junho 25, 2008

Hoje esbarrei com uma entrevista bem interessante com o Bjarne Stroustrup, que para quem não sabe, é quem inventou (ou criou) o C++.

Na entrevista ela fala algumas coisas sobre como o C++ foi criado, como tem sido usado e como se deve usa-lo. Também comenta um pouco sobre as mudanças que estão por vir no C++0X.

Aproveitando a deixa, aqui vão alguns links sobre C++ que acho bem interessantes e tem bastante coisa bacana:


Auto Pointer (auto_ptr)

Junho 10, 2008

Para ver o artigo anterior da série: Smart Pointer - Introdução

Agora que já vimos o funcionamento básico de um smart pointer, vamos ver como usar um smart pointer que vem junto com a biblioteca padrão do C++.

Um ponteiro automático (auto_ptr) é semelhante a classe de smart pointer que mostrei no post anterior. Ele mantem o ponteiro para o dado em uma variável interna e quando seu destrutor é chamado ela se encarrega de destruir (fazer um delete) no ponteiro.

Mas a nossa classe de smart pointer tem um serio problema, se fizermos:


void funcao()
{
	Pointer p = new int(0);
	Pointer p2 = p;
}

//ou entao

Pointer alocaInt(int v)
{
	return(Pointer(new int(v));
}

O código acima simplesmente vai dar problemas, porque o int alocado com new vai ser “deletado” duas vezes,a primeira por p e a segunda por p2! Ops! Para evitar isso, podemos solucionar o problema usando algumas técnicas como:

  • Declarar o construtor de cópia e operador de atribuição como privados, bloqueando assim cópias de ponteiros (não resolve o problema, mas evita que ele ocorra).
  • Gerenciar os ponteiros para que apenas uma instância do objeto Pointer apague ele (usando contagem de referencia por exemplo).
  • Simplesmente quando um pointer for inicializado usando construtor de cópia ou operador de atribuição, ao invés de copiarmos os valores, vamos move-los (solução do auto_ptr).

Todas as soluções acima possuem seus prós e contras, não vou discutir aqui qual é melhor ou pior, porque cada uma acaba sendo melhor que a outra em determinados problemas. A solução do auto_pointer (que consiste em mudar o “dono” do ponteiro) é melhor (por este aspecto apenas) que a contagem de referencia por não precisar alocar um objeto extra para contar referencias ou criar um membro na classe para tal fim. Mas em contra-partida, não permite tanta flexibilidade quanto esta (não podemos usar o auto_ptr com conteiners da STL, por exemplo).

Se você simplesmente alocar um objeto na heap, fazer algumas operações e depois destruí-lo, o auto_ptr é uma das melhores soluções, pois uma boa implementação não traz overhead algum. Exemplo:


#include <memory>
#include <iostream>

int main(int argc, char **argv)
{
    using namespace std;

    auto_ptr<int> p(new int(3));

    cout << p.get() << endl;

    auto_ptr<int> p2 = p;

    cout << p.get() << endl;
    cout << *p2 << endl;

    return(0);
}

No incrível exemplo acima, primeiro criamos um auto_ptr p e alocamos um inteiro. Após imprimir o valor do ponteiro, criamos um novo auto_ptr p2 e inicializamos ele com p. Nesse momento, p2 passa a apontar para o inteiro alocado anteriormente (agora ele é o dono) e p simplesmente aponta para NULL.

No final do código imprimimos o valor do ponteiro p e o valor apontado por p2 (o inteiro 3).

Outro exemplo:


#include <memory>
#include <iostream>

using namespace std;

class Objeto
{
      public:
             Objeto(int v): m_valor(v){}

             ~Objeto()
             {
                          cout << "obj " << m_valor << " morreu" << endl;
             }

             int valor(){ return m_valor; }
      private:
              int m_valor;

};

auto_ptr<Objeto> alocaObj(int v)
{
    return(auto_ptr<Objeto>(new Objeto(v)));
}

int main(int argc, char **argv)
{
    using namespace std;

    try
    {
        auto_ptr<Objeto> p = alocaObj(5);

        cout << p->valor() << endl;

        alocaObj(10);

        throw "danou-se";
    }
    catch(...)
    {
        cout << "ops!!!" << endl;
    }

    return(0);
}

Esse exemplo é muito mais bacana que o anterior! Tem até exceções! O legal desse exemplo é que podemos ver que não é preciso se preocupar em destruir qualquer um dos objetos alocados, porque o auto_ptr se encarrega disso. Note também que caso alguem lance um exceção, o auto_ptr não nos deixa na mão e se encarrega de limpar a sujeira.

Outro detalhe também é que na segunda chamada a alocaObj nós nem mesmo nos preocupamos em pegar o valor de retorno, e o auto_ptr mesmo assim deu conta do recado!

Conclusão

Para casos simples onde precisamos apenas controlar a vida de um objeto no escopo de uma função ou entre chamadas de funções, ao invés de usarmos delete manualmente (sujeito a erros, pois em algum fluxo podemos esquecer de usa-lo) o auto_ptr vem a calhar como uma ferramenta simples e eficiente. No próximo post vamos começar a ver as classes de smart_pointer da boost, que podemos utilizar inclusive com a STL!

Próximo artigo da série: Boost Shared Pointer

Referências

Using auto_ptr Effectively

Wikipedia


Smart Pointer - Introdução

Maio 29, 2008

Smart Pointers (ou ponteiros inteligente) são objetos em C++ cuja a função básica é monitorar ponteiros e desalocar os mesmos (dando um delete) quando estes não são mais utilizados. Mas como um Smart Pointer sabe a hora de destruir um objeto? Isso depende da implementação, algumas das técnicas utilizadas são:

  • Contagem de referencias: Para cada alocação de memória, o smart pointer aloca um outro objeto que ele usa para contar quantos smart pointers apontam para a mesma região de memória, esse objeto é compartilhado entre todos os smart pointers que utilizam a mesma região de memória. Sempre que um novo smart pointer passa a apontar para uma região de memória, ele incrementa esse contador, quando ele é destruído, decrementa o contador. O ultimo smart pointer destruído (aquele que decrementar um contador e este chegar a zero) se encarrega de desalocar a memória.
  • Controle de escopo: Essa implementação é a mais simples, ela se baseia no fato que todo objeto em C++ no final da sua vida, tem seu destrutor executado. Os ponteiros que fazem controle por escopo, simplesmente no seu destrutor destroem a região de memória para qual apontam. Esse tipo de ponteiro é apenas utilizados para controlar objetos que não costumam ser compartilhados, como por exemplo alocar um buffer dentro de uma função ou uma classe que possui como membro um objeto que precisa ser alocado dinamicamente.

Funcionamento Básico

Um smart pointer tira proveito do fato que todo objeto em C++ criado na pilha vai ter seu destrutor executado, tirando proveito dessa funcionalidade podemos criar o código a seguir:


class IntPointer
{
    public:      
        IntPointer(int *p):pValue(p) {assert(p);}
        ~IntPointer(){ delete pValue; }

        int *GetValue() { return pValue; }

    private:
        int *pValue;
};

A classe acima simplesmente é inicializada com um ponteiro para inteiros e no seu destrutor se encarrega de liberar a memória, podendo ser utilizada como no exemplo abaixo: 


int main(int, char **)
{
    IntPointer ptr(new int(5));

    using namespace std;

    cout << "Valor: " << *ptr.GetValue() << endl;

    return(0);  

}

 

Criando uma Classe Genérica

Note que no código anterior não nos preocupamos em desalocar a memória alocada no inicio da função. Mas existe um problema: E se quisermos armazenar floats ou qualquer outro tipo de dado? Podemos modificar o código com o uso de templates para que fique genérico:


template <typename  T>
class Pointer
{
    public:
        Pointer(T *p): pValue(p) {assert(pValue);}
        ~Pointer() { delete pValue; }

        T *GetValue() {return pValue; } 

     private:
        T *pValue;
};

Agora podemos usar o código acima com qualquer tipo de dado primitivo, voltando ao exemplo anterior:


int main(int , char **)
{
    Pointer<int> ptr(new int(5));
    Pointer<float> ptrf(new float(5.3f));

    using namespace std;<span> </span>
    cout << "Valor: " << *ptr.GetValue() << endl;
    cout << "Valor float: " << *ptrf.GetValue() << endl;  

    return(0);
}

A classe Pointer ainda não é tão inteligente assim, mas podemos também usar tipos de dado mais complexos:


struct Person
{
    std::string name;
    int age;
};

int main(int , char **)
{
    Pointer<Person> ptr(new Person());

    ptr.GetValue()->name = "SmartPointer";

    using namespace std;
    cout << ptr.GetValue()->name << endl;

    return(0);
};

Sobrecarga de Operadores

Um item inconveniente da classe Pointer é o fato de que é necessário chamar o método “GetValue” sempre que é preciso acessar o ponteiro que ela armazena. Para abstrair isso, podemos usar sobrecarga de operadores e modificar o ponteiro para que ele fique um pouco mais esperto:


template <typename  T>
class Pointer
{
    public:
        Pointer(T *p): pValue(p) {assert(pValue);}
        ~Pointer() { delete pValue; }

        T *operator->()
        {
            return(pValue);
        }

    private:
        T *pValue;
};

//Agora podemos escrever:
int main(int , char **)
{
    Pointer<Person> ptr(new Person());

    ptr->name = "SmartPointer";

    using namespace std;
    cout << ptr->name << endl;

    return(0);
};

Conclusão

Com essa pequena classe temos uma implementação bem simples de um smart pointer (ela tem alguns problemas, como não tratar const corretamente, não funciona se o objeto pointer for copiado, arrays, etc), sendo assim, esta classe não serve para muita coisa a não ser como exemplo simples (digamos que não podemos chamar ela de smart).

Um detalhe interessante é que usando corretamente inlines e compilando o código acima em um bom compilador (com otimizações ligadas) usar essa classe de Pointer não traz sobrecarga nenhuma ao programa, não teremos nenhum custo adicional de performance ou consumo de memória usando esse tipo de construção. No Visual Studio, por exemplo, a classe inteira desaparece com otimizações ligadas, ficando apenas a chamada de delete que desaloca a memória.

Por hoje é apenas isso, mas no próximo post, vamos ver uma classe de smart pointer que faz parte da biblioteca padrão do C++: auto_ptr.

Próximo artigo da série: Auto Pointer (auto_ptr)


Compilando a Boost no Windows (usando Visual Studio)

Maio 14, 2008

Como pretendo falar um pouco sobre o uso de smart pointers, e pretendo fazer isso utilizando as implementações existentes na Boost, então o primeiro passo é instalar a Boost.

Mas primeiramente, o que é a Boost? A Boost é uma coleção de bibliotecas para o C++ desenvolvido por diversas pessoas, seguindo rigorosos critérios de qualidade e portabilidade, sendo que é possível utilizar a Boost na maioria dos compiladores modernos. A Boost reúne bibliotecas como:

  • Smart Pointers
  • Tipos Variant
  • File System
  • Operações com Imagens
  • Threads
  • e muito mais…

Para melhores informações, acesse o site da Boost, e aproveite e já faça download do pacote com o código.

A seguir vamos ver como compilar a Boost (note que para usarmos alguns componentes da Boost, como smart pointers, não é necessário compila-la, mas porque não aproveitarmos a oportunidade e deixar tudo pronto?)

Para as instruções abaixo, estou assumindo que o usuário possui um Visual Studio instalado (e no caso das versões Express, possui a Platform SDK instalada) 

Configurando o Environment

Caso você não use as versões express do Visual, pode pular essa parte. Caso contrário, é necessário configurar algumas variáveis de ambiente antes de prosseguir. No Windows, clique em Iniciar -> Painel de Controle -> Sistema. Escolha a TAB avançado, e clique em variáveis de ambiente. Na parte de variáveis do sistema, procure por “INCLUDE”. 

Caso não exista, clique em “Novo” e entre com os dados:

Nome: INCLUDE

Valor: C:\Arquivos de Programas\Microsoft Platform SDK for Windows Server 2003 R2\Include

Tenha certeza, que o caminho é o caminho valido para a Platform SDK instalada em sua maquina, e inclua o diretório Include.

Caso a variável já exista, clique em “Editar”, na parte do valor, vá para o final e entre com o mesmo valor usado acima, mas inclua  um “;” antes, exemplo:

Valor: c:\develop\libs\loki\include;C:\Arquivos de Programas\Microsoft Platform SDK for Windows Server 2003 R2\Include

Criada a variável INCLUDE, é preciso então criar a variável LIB, basta seguir o o mesmo procedimento acima, e no valor entre:

C:\Arquivos de Programas\Microsoft Platform SDK for Windows Server 2003 R2\Lib

Novamente, tenha certeza de que o valor acima é o correto para o seu sistema. Vá clicando em “OK para salvar as configurações e vamos continuar.

Compilando

Após baixar o código, descompacte o arquivo para o seu diretório de libs (caso não tenha um ou não sabe o que é isso, basta criar um diretório qualquer em seu computador que você deve utilizar para as bibliotecas). No meu caso, a Boost é descompactada para c:\develop\libs\, resultando em c:\develop\libs\boost_1_35_0. 

Após descompactar os arquivos, é necessário copiar o Bjam, que é a ferramenta usada para compilar a boost. O Bjam pode ser encontrado no site da Boost. Após copiar o Bjam (a versão executável, se você baixou o código apenas, vai ter que compila-lo antes de usar). Descompacte o Bjam e coloque no mesmo diretório de instalação da Boost (no meu caso c:\develop\libs\boost_1_35_0).

Tendo instalado o código e o Bjam, é hora de acessar o prompt de comando do Visual Studio, basta clicar em Iniciar -> Programas -> Visual C++ 2005 Express Edition -> Visual Studio Tools -> Visual Studio 2005 Command Prompt. Note que esse é o caminho para Visual Studio 2005 Express, se você usa alguma outra versão, o caminho vai variar, mas o importante é acessar o prompt de comando do Visual, não o padrão do Windows.

Dentro do prompt, basta ir para o diretório da Boost (no meu caso, digitando: cd develop\libs\boost_1_35_0), chegando no diretório, precisamos chamar o Bjam passando como parâmetro o toolset a ser usado (no caso do visual, msvc), mas antes é preciso saber a versão do visual:

  • Visual Studio Express 2005 ou Visual Studio C++ 2005: 8.0
  • Visual Studio Express 9.0: 9.0

Agora basta invocar o Bjam usando o toolset e a versão correta:

  • Visual 2005: bjam toolset=msvc-8.0
  • Visual Studio 2009: bjam toolset=msvc-9.0

E esperar … 

Após concluído o build a Boost esta pronta para ser usada. 

No próximo post vou começar a mostrar como usar os smart pointers da boost e outros componentes.


Wincatalog 2008

Maio 7, 2008

Se você é como eu e possui dezenas de CDs e DVDs cheios de arquivos, artigos, samples, códigos, libs, tools, etc; e sempre quando você precisa de algum desses arquivos nunca consegue encontrar o maldito CD, ou simplesmente tem preguiça de ficar procurando em todos os CDS (porque muitas vezes é mais fácil baixar de novo o arquivo). Então instale o WinCatalog 2008!

Este programa permite que você cadastre todos os seus CDs, DVDs, disquetes (alguem ainda usa isso?), e feito o cadastro, possui um sistema de busca, que facilita muito a vida na hora de procurar aquele arquivo sumido. Identificado o CD, você pode então pegar ele na pilha (estou assumindo que você ao menos numerou os seus CDs) e copiar o arquivo!

Tem a versão light que é totalmente free, mas a versão 2008 (que na verdade é free se você utilizar apenas para avaliação, limitado a 5 volumes por arquivo) tem muito mais recursos:

  • Pastas virtuais: com isso você podem organizar todos os seus arquivos em uma estrutura de diretório única, achei bem útil isso, é como se todos os arquivos estivessem juntos.
  • Objetos pessoais: você pode criar outros objetos para armazenar, como por exemplo, sua lista de livros (de papel) dentro do programa, para que? Simples, você pode cadastrar seus amigos e gerenciar empréstimos!
  • Processa arquivos zip: ele permite que você veja o que tem dentro daqueles arquivos zip com nomes estranhos que você guardou a 5 anos atrás sabe la deus porque!

Caso queira experimentar: http://www.wincatalog.com/standard.html


Valve Steamworks

Maio 2, 2008

Steamworks

A Valve Software liberou esse semana a API do Steamworks, que é uma API que pode ser usada para:

  • Coletar estatísticas do jogo (como por exemplo média de pontos de um jogador)
  • Autenticação para Multiplayer
  • Controle de partidas (Matchmaking)
  • Gerenciar comunidade
  • Rede (vide sockets  :) )
  • Sistema anti-chear
  • Comunicação por voz
  • DRM (Digital Rights Management ou controle de pirataria)

Pelo site tudo é de graça e qualquer um pode utilizar, não vi ainda as entre linhas e nem testei, mas parece interessante: https://partner.steamgames.com/