Gerência automática de memória é excelente por um lado, mas como o usuário não tem controle sobre quando a limpeza da memória vai ser feita, podemos ter comportamentos estranhos no nosso programa. Em certas aplicações, como as de tempo real, queremos ter garantias sobre o tempo de execução, por isso a gerência manual da memória ainda é relevante.
Porém, como o programado é um ser humano (em geral), ele comete erros. Principalmente quando o projeto começa a ficar muito grande. Então temos as temidos vazamentos de memória. Esse fenômeno acontece quando criamos um objeto com o operador new, mas esquecemos de deletá-lo. Então aquele setor da memória fica ocupado e o programa não consegue mais utilizá-lo.
Outro problema acontece quando múltiplas partes do código compartilham o mesmo objeto contido na memória dinâmica. De quem é a responsabilidade de deletá-lo? um método pode deletar um objeto que ainda está sendo utilizado por outro método.
Para solucionar tudo isso, C++11 contém ponteiros inteligentes, que são estruturas que garantem que os dados da memória serão liberados quando não forem mais necessários.
Nesse artigo, vamos aprender sobre o primeiro tipo de ponteiro inteligente: unique_ptr. Esses tipos de ponteiros garantes que apenas um ponteiro vai conter a referência ao objeto de cada vez. Para que se possa passar o objeto adiante, deve-se "liberá-lo" do ponteiro.
Hora de ver unique_ptr em ação. Vamos primeiro definir um objeto que exibe uma mensagem quando está sendo deletado.
class Data{ public: void aMethod(){ cout << "method called" << endl;} ~Data(){ cout << "data is being deleted" << endl; } };Agora quando o ponteiro sai fora do escopo, o objeto associado é deletado.
{ unique_ptr<Data> dataPtr { new Data() }; }Para passar um objeto adiante, o método release deve ser usado. Assim temos a garantia que o objeto só tem um dono. A saída do programa abaixo seria o primeiro ponteiro sendo 0 (porque já liberou o objeto) e o segundo sendo igual ao ponteiro para o objeto.
unique_ptr<Data> dataPtr = unique_ptr<Data>(new Data()); unique_ptr<Data> dataPtr2 {dataPtr.release()}; cout << dataPtr.get() << " and " << dataPtr2.get() << endl;Ponteiros inteligentes se comportam como ponteiros normais, então é possível chamar métodos, acessar elementos de um vetor, etc. Abaixo um exemplo da chamada de um método.
dataPtr2->aMethod();Finalmente, uso mais interessante de ponteiros inteligentes é o retorno de objetos criados dentro de funções. O retorno de um ponteiro inteligente garante que os clientes da função tem que usar um ponteiro inteligente para receber os dados.
unique_ptr<int> createOne(){ return unique_ptr<int>(new int(1)); } // [...] unique_ptr<int> one_p = createOne(); cout << *one_p << endl;O código completo pode ser visto no github, em unique_ptr.cpp.
Nenhum comentário:
Postar um comentário