Ponto V!

Home C/C++ Smart Pointers Smart Pointer - Introdução
Bruno Crivelari Sanches
Smart Pointer - IntroduçãoImprimir
Escrito por Bruno Crivelari Sanches

Smart Pointers (ou ponteiros inteligentes) 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;
    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 tutorial, vamos ver uma classe de smart pointer que faz parte da biblioteca padrão do C++: auto_ptr.


Comentários (14)
  • Neto  - Erro
    avatar

    Há um erro singelo de sintaxe! No exemplo de criar a classe genérica o sinbolo ">" não foi adicionado, ao invés dele adicionou-se por engano "

  • Vinícius Godoy de Mendonça  - Corrigido
    avatar

    Obrigado, já está corrigido!

  • Oberlan  - Matriz
    avatar

    Teria como declarar uma matriz usando smart pointer?
    Obrigado!!!

  • Bruno Crivelari Sanches
    avatar

    Tipo:

    Pointer ptr[5][5];

    Uma matriz de smartpointers...

    T+

  • Oriundus  - Tente vc mesmo
    avatar

    Tente você mesmo Oberlan.
    Tem um erro na sua frase Neto, sinbolo é com M -> siMbolo

  • Bruno
    avatar

    Quando eu entro pela CPP não sei em que clicar, ou SMART POINTER ou Visual C++, EXISTEM DIFERENCIAS? e outra coisa.. parei de usar o Visual Basic e o Studio, estou usando o Code Blocks.. queria tirar essas dúvidas, teriam diferencias nos dois casos?

    :confused:

  • Bruno Crivelari Sanches
    avatar

    São apenas categorias para separar artigos relacionados, embaixo de cada uma tem a descrição do que é, existem diferenças, senão não tinha sentido separar em categorias.

    O Code Blocks é uma outra IDE de desenvolvimento, tem diferenças em relação ao Visual, mas a linguagem C++ é a mesma, só muda a ferramenta.

    T+

  • Thomas
    avatar

    Errei, meu nome é Thomas. :P

  • Dante  - Dúvida QT/SmartPointers
    avatar

    Olá Bruno, fiz um teste com sua classe Pointer, porém não a IDE/compilador não acharam o método assert().

    Estou trabalhando com QT 4.8 + GCC. O que seria este assert?

    Atenciosamente,

  • Bruno Crivelari Sanches
    avatar

    Olá Dante,

    bom, use apenas para testes, pois essa versão é bem simples e não cobre vários casos.

    De qualquer forma, o assert é um macro do C que valida algo, se não for validado, ele aborta execução do programa. O detalhe é que ele só faz isso na versão debug do programa, na versão release ele é vazio.

    É usado para testarmos condições "impossíveis" em um programa durante o dev, isso significa que se o assert for invocado, temos um bug.

    Basta fazer #include <assert.h> que você vai ter ele.

    T+

  • Dante
    avatar

    Obrigado pelo esclarecimento.

  • Carlos  - Parabens
    avatar

    :lol: Ótimo tutorial estão de parabéns, só uma duvida há bibliotecas que facilita na hora de criar ponteiros, não li os comentários a cima e ainda não li os próximos tutoriais talvez você ja tenha até comentado, mas eu gostaria de saber qual biblioteca você recomendaria se algum ja supera o Boost obrigado Bruno.

  • Bruno
    avatar

    Eu só utilizo a boost. O que mudou desde a publicação desses artigos (e o ideal seria atualizar eles) é que o C++0x já inclui os smart pointers da boost. Então ao invés de usar a boost, se seu compilador suportar, você pode usar a biblioteca padrão.

  • Anônimo  - re:
    avatar
    Bruno Escreveu:
    Eu só utilizo a boost. O que mudou desde a publicação desses artigos (e o ideal seria atualizar eles) é que o C++0x já inclui os smart pointers da boost. Então ao invés de usar a boost, se seu compilador suportar, você pode usar a biblioteca padrão.

    Não sabia disso, vou dar uma pesquisadinha. Obrigado. :lol:

Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter