Para ver o artigo anterior da série: Boost Scoped Pointer
Todo os smart pointers que mostrei até agora tem um problema que ainda não vimos como resolver: eles não funcionam com arrays! Isso acontece pois como todo programador C++ sabe (sabe mesmo não é?) devemos usar o delete[] para liberar a memória de um array, por exemplo:
#include <string>
#include <iostream>
class Person
{
public:
inline void SetName(const std::string &name)
{
m_Name = name;
}
~Person()
{
using namespace std;
cout << "Fui destruido: " << m_Name << endl;
}
private:
std::string m_Name;
};
int main(int, char **)
{
Person *p = new Person[10];
delete []p;
return(0);
}
Se ao invés do delete[], for usado um delete comum, geralmente a memória é liberada corretamente, mas somente o destrutor do primeiro elemento do array é executado. Isto ocorre porque o delete comum (e o compilador na maioria das vezes) não tem como saber se um ponteiro aponta para um elemento apenas ou para um array. Então precisamos de alguma forma avisar eles sobre o array. Para fazer o teste, coloque um printf no destrutor da classe Person e modifique o exemplo acima para rodar o delete normal.
Mas como o compilador sabe quantos elementos destruir? Isso depende, uma implementação que pode ser usada é toda vez que for alocado um array, alocar antes dele um int e colocar nele o tamanho do array, assim o delete poderia ser implementado +/- assim:
template <typename T>
operator delete[](T *ar)
{
int num = ((int *)ar)-1;
for(int i = 0;i < num; ++i)
ar[i].~T();
free(ar);
}
Alguns compiladores usam técnicas semelhantes a esta, mas não existe nenhuma garantia sobre qual a técnica a ser utilizada, isso depende da implementação. Agora porque não incluir em todos os ponteiros alocados um int com a quantidade de objetos? Bom, simplesmente porque senão toda a alocação teria esse ônus de um int a mais (acredito que devam existir outros problemas, mas isso não vem ao caso), que poderia atrapalhar a vida de alguns programas, dessa forma, seguindo a filosofia do C++ “Você não paga pelo o que não usa” temos como resultado dois operadores de delete.
Smart Pointers e Arrays
Para contornar esse problema das arrays, a boost criou então o shared_array, que é usado especificamente para arrays. Seu uso é quase o mesmo do shared_ptr:
#include <boost/shared_array.hpp>
#include <string>
int main(int, char **)
{
const int NUM = 10;
boost::shared_array<Person> p(new Person[NUM]);
using namespace std;
for(int i = 0;i < NUM; ++i)
p[i].SetName(string("Bruno"));
return(0);
}
Não tem segredo! Existe um outro smart pointer para arrays, o scoped_array, que tem comportamento idêntico ao scoped_ptr, mas pode ser usado com arrays:
#include <boost/scoped_array.hpp>
#include <string>
int main(int, char **)
{
const int NUM = 10;
boost::scoped_array<Person> p(new Person[NUM]);
using namespace std;
for(int i = 0;i < NUM; ++i)
p[i].SetName(string("bcs"));
return(0);
}
Novamente não mudou nada no código, apenas o nome do smart pointer usado. Mas lembre-se que nos bastidores tivemos algumas mudanças, pois o shared_array precisa assim como o shared_ptr alocar um objeto para contagem de referencia, já o scoped_array (da mesma forma que o scoped_ptr) não precisa de nada disso.
No próximo post, vamos ver a ultima classe de smart pointer da Boost, o intrusive_ptr.
Próximo artigo da série: Boost Intrusive Pointer
maio 8, 2009 às 7:51 |
[...] Boost Shared Array e Scoped Array [...]