logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

follow us in feedly

Resolvendo o bug usando um smart pointer feito em casa

Lembra do bug? Solucionei o problema criando um copy constructor (construtor chamado quando um objeto é inicializado com o valor de outro) para a nossa classe CTest2, de forma que em caso de cópia um novo objeto fosse criado. Isso resolveu o nosso problema para essa classe específica.

Minha segunda solução é criar um smart pointer (template que se comporta exatamente com o ponteiro original) que faça a cópia. Assim, não é mais necessário implementar o copy constructor só porque a classe tem um ponteiro como membro. Se o objeto apontado por esse ponteiro deve ter o tempo de vida igual ao do objeto que o contém (o que é o nosso caso), isso funciona como um luva.

Nosso smart pointer além de gerenciar cópia, ainda desaloca o objeto automaticamente. Sim, a mesma coisa que muitas runtimes fazem ao custo do seu programa ficar muito mais lento... Bom, vamos ao código, porque ele sempre vale mais do que 6378 palavras:

#include <iostream>
#include <vector>
#include <string>

#ifndef DWORD
#define DWORD unsigned int
#endif

//
// nosso template para copiar o objeto apontado
//
template <typename T>
class CopyPointer
{
public:
   T* p;

   CopyPointer()
   {
      p = NULL;
   }

   ~CopyPointer()
   {
      Free();
   }

   void Alloc()
   {
      Free();
      p = new T();
   }

   void Free()
   {
      if(p)
      {
         delete p;
         p = NULL;
      }
   }

   void Attach(T* pT)
   {
      if(pT == p)
         return;

      Free();
      p = pT;
   }

   T* Detach()
   {
      T* tempp = p;
      p = NULL;

      return tempp;
   }

   //
   // copy constructor. Quando for copiado dessa forma,
   // vamos copiar o objeto ao invés de copiar o ponteiro
   //
   CopyPointer(const CopyPointer<T>& c)
   {
      //
      // isso vai chamar o copy constructor de T
      //
      p = new T(*c.p);
   }

   //
   // isso permite que haja cópia a partir de um ponteiro raw, não
   // só pelo nosso smart pointer
   //
   CopyPointer(const T* pT)
   {
      p = new T(*pT);
   }

   CopyPointer& operator=(T* pT)
   {
      if(p == pT)
         return *this;

      if(p)
         *p = *pT;
      else
         p = new T(*pT);

      return *this;
   }

   CopyPointer& operator=(const CopyPointer<T>& pT)
   {
      if(p == pT.p)
         return *this;

      if(p)
         *p = *pT.p;
      else
         p = new T(*pT.p);

      return *this;
   }

   operator T*() const
   {
      return p;
   }

   T* operator->() const
   {
      return p;
   }

   bool operator!() const
   {
      return (p == NULL);
   }

   bool operator<(T* pT) const
   {
      return p < pT;
   }

   bool operator==(T* pT) const
   {
      return p == pT;
   }
};


class CTest1
{
public:
   CTest1() : dwValue(0)
   {}

   DWORD dwValue;
   std::string strValue;
};



class CTest2

{
private:
   CopyPointer<CTest1> m_pTest1;
public:

   CTest2()
   {
      m_pTest1.Alloc();
   }
   
   //
   // Não preciso mais de um destrutor para desalocar o ponteiro.
   // E nem precisei usar um linguagem mais limitada ou uma runtime lenta
   //

   CTest2(DWORD dw, const std::string& str)
   {
      m_pTest1.Alloc();

      m_pTest1->dwValue = dw;
      m_pTest1->strValue = str;
   }

   CTest1* GetTest1() const
   {
      return m_pTest1;
   }

};

int main(int argc, char* argv[])
{
   std::vector<CTest2> vecTest2;
   
   CTest2 t1, t2;

   t1.GetTest1()->dwValue = 50;
   t1.GetTest1()->strValue = "baba";

   //
   // chamando o copy constructor
   //
   CTest2 t3 = t1;

   //
   // chamando operator=
   //
   t2 = t3;

   
   //
   // vamos colocar um CTest2 no vetor. Isso faz uma cópia
   //
   vecTest2.push_back(CTest2(100, "1bit"));

   //
   // agora vamos pegá-lo e ver o seu valor
   //
   std::cout << "dwValue = "  << vecTest2[0].GetTest1()->dwValue  << std::endl;
   std::cout << "strValue = " << vecTest2[0].GetTest1()->strValue << std::endl;


   return 0;
}

Só não tenho certeza do "const correctness" do meu smart pointer. Alguém se habilita?


Em 02/05/2005 00:54, por Rodrigo Strauss


  
 
 
Comentários
Thiago | website | e-mail | em 06/05/2005 | #
Eu estava trabalhando justamente num problema assim só que um pouquinho diferente!
O problema é o seguinte:

// tenho uma classe base
class Base { virtual void F()=0; };

// e uma ou mais derivadas
class Derived : public Base {
virtual void F(){ std::cout << "Derived"; }
};

class CopiableClass{
...
Base *p;
...
}
Como gerar o copy contructor automático para a classe CopiableClass?
A solução mais comum deste tipo de problema é a classe base implementar um "virtual constructor" que geralmente é chamado clone();
O que eu quero fazer é gerar um "CopyPointer" para resolver este tipo de problema automaticamente.

Coloquei o código preliminar no meu site:
http://paginas.terra.com.br/informatica/thiago_adams/value.h...

A idéia original não era para ponteiros, mas agora estou pensando em alterar justamente para suportar classes base abstratas.
Na minha opnião seria uma classe muito útil para diversos problemas.
Podemos trocar idéias para gerar esta classe...
Algo a dizer?
Nome:


Site:


E-mail:


Escreva o número vinte e seis:


 Não mostre meu e-mail no site, não serve pra nada mesmo...

Comentário





Os comentários devem ser sobre assuntos relativos ao post, eu provavelmente apagarei comentários totalmente offtopic. Se quiser me enviar uma mensagem, use o formulário de contato. E não esqueça: isso é um site pessoal e eu me reservo o direito de apagar qualquer comentário ofensivo ou inapropriado.
rebarba rebarba
  ::::