Ajuda com polimorfismo (Copia de nova instancia)

17 views
Skip to first unread message

MARCOS UBIRAJARA

unread,
Feb 24, 2012, 6:12:31 AM2/24/12
to ccppb...@googlegroups.com
Bom dia pessoal,
 
Eu estou com o seguinte problema com polimorfismo:
 
class A {
 
};
class B : public A
{
       public:
         int x;
};
class C: public A
{
      public:
         int z;
 
};
A * makeCopy(A * a)
{
      //A questão é: Como criar uma nova copia de *a, sendo que em *a podem existir objetos do tipo A,B ou C?
 
}
int main()
{
          A * a = new B();
          A  * aa =     makeCopy(a);
}
 
Novamente muito grato aos mestres Maicon, Thiago, Vinicius, Vander, Reuben, Alisson, P., Mazzarino, Felipe Almeida,Gianni, Doublas, .... e toda a galera do cppbrasil, estou acompanhando as mensagens e aprendendo muito com todos vocês.
 
MArcos

Ponto V! - Vinícius Godoy

unread,
Feb 24, 2012, 6:41:37 AM2/24/12
to ccppb...@googlegroups.com
Nesse caso, há algumas alternativas:

a) Sobrecarga. Crie as funções:
A* makeCopy(B* value)
{
   //Faz a cópia de B
}
A* makeCopy(C* value)
{
   //Faz a cópia de C
}

b) Polimorfismo. Você pode forçar que o objetos da classe A sejam copiáveis:
class A {
    virtual A* makeCopy() const = 0;
    virtual ~A() {}
};

Em seguida, implemente o método makeCopy() nas classes filhas, fazendo cada uma copiar a si mesmo.

Finalmente, o uso fica assim:

int main()
{
          A * a = new B();
          A  * aa = a.makeCopy(); //Chama o método de B
}

[]s,
Vinícius Godoy de Mendonça
@vinigodoy

Ponto V! - Programação de Jogos Profissional
www.pontov.com.br - @pontov - Facebook




--
Antes de enviar um e-mail para o grupo leia:
http://www.ccppbrasil.org/wiki/Lista:AntesdePerguntar
--~--~---------~--~----~---------------------------------~----------~--~----~
[&] Colabore com a Pesquisa de Preferência de Conteúdo
para Eventos do Grupo C & C++ Brasil:
http://www.surveymonkey.com/s/GBBGTXN
------~----~-------~---~---~---~---~----------------~------------~---------~
[&] C & C++ Brasil - http://www.ccppbrasil.org/
Para sair dessa lista, envie um e-mail para ccppbrasil-...@googlegroups.com
Para mais opções, visite http://groups.google.com/group/ccppbrasil
--~--~---------~--~----~--~-~--~---~----~-----------------~--~----------~
Emprego & carreira: vag...@ccppbrasil.org
http://groups.google.com/group/dev-guys?hl=en

Marcelo Geyer

unread,
Feb 24, 2012, 7:04:09 AM2/24/12
to ccppb...@googlegroups.com
Dependendo da necessidade, você pode dar uma olhada no construtor de cópia e ajustar o código.

Abraços,

Marcelo E. Geyer
--
Marcelo E. Geyer
Standard Net Tecnologia e Informação

Ponto V! - Vinícius Godoy

unread,
Feb 24, 2012, 7:09:42 AM2/24/12
to ccppb...@googlegroups.com
Nesse caso, também seriam exemplos de sobrecarga, certo? Pois você teria que elaborar um construtor para cada tipo da classe filha.

O C++ não tem reflexão, portanto, não é possível (pelo menos, não de forma trivial sem RTTI ou sem usar recursos mais macabros) testar pelo tipo do objeto apontado pelo ponteiro.

Att,

Vinícius

Уθя¡ςκ

unread,
Feb 24, 2012, 7:12:19 AM2/24/12
to ccppbrasil
Uma correção, opção 'a' não funcionaria especificamente no código:

A * a = new B();
A * aa = makeCopy(a);

Não há como o compilador saber em tempo de compilação que tipo o
ponteiro a aponta, então só a primeira versão do método makeCopy
seria chamada.

On Feb 24, 9:41 am, Ponto V! - Vinícius Godoy

Уθя¡ςκ

unread,
Feb 24, 2012, 7:15:04 AM2/24/12
to ccppbrasil
método não, nesse caso, função.

Thiago Adams

unread,
Feb 24, 2012, 7:53:03 AM2/24/12
to ccppbrasil

On Feb 24, 9:12 am, MARCOS UBIRAJARA <marcosubiraj...@ig.com.br>
wrote:
> Bom dia pessoal,
>
> Eu estou com o seguinte problema com polimorfismo:
>
> class A {
>
> };
>
> class B : public A
> {
>        public:
>          int x;};
>
> class C: public A
> {
>       public:
>          int z;
>
> };
>
> A * makeCopy(A * a)
> {
>       //A questão é: Como criar uma nova copia de *a, sendo que em *a podem
> existir objetos do tipo A,B ou C?
>
> }
>
> int main()
> {
>           A * a = new B();
>           A  * aa =     makeCopy(a);
>
> }


A solução clássica é usar um método virtual "clone".
Ele é um "constructor virtual"

class A
{
public:
virtual A* clone() = 0;
};

class B : public A
{
virtual B* clone()
{
return new B(*this);
}

public:
int x;

};

class C: public A
{
public:

virtual C* clone()
{
return new C(*this);
}
int z;
};


int main()
{
A* a = new B();
A* aa = a->clone();
}

O método clone é também um dois poucos casos aonde
eu vi usar a covariancia do retorno no C++.
Ou seja, embora cada um retorne um tipo diferente
um retorna A, outro B outro C, eles são considerados
o mesmo método virtual "clone" pela propriedade da covariancia.


P.

unread,
Feb 24, 2012, 7:55:59 AM2/24/12
to ccppb...@googlegroups.com

Em sexta-feira, 24 de fevereiro de 2012 10h09min42s UTC-2, ViniGodoy escreveu:
Nesse caso, também seriam exemplos de sobrecarga, certo? Pois você teria que elaborar um construtor para cada tipo da classe filha.

O C++ não tem reflexão, portanto, não é possível (pelo menos, não de forma trivial sem RTTI ou sem usar recursos mais macabros) testar pelo tipo do objeto apontado pelo ponteiro.


Eis como C++ testa o tipo de objeto apontado pelo ponteiro para implementar makeCopy (pouco) genérica.

A* makeCopy (A* source)
{
    if (dynamic_cast<B*>(source) != nullptr) return makeCopyB(source);
    else if (dynamic_cast<C*>(source) != nullptr) return makeCopyC(source);
    else return nullptr;
}

O problema com esta técnica é jogar fora um benefício do polimorfismo: a definição de makeCopy precisa conhecer de antemão todas as classes concretas na hierarquia. Isso é viável se o uso do polimorfismo não tem por objetivo a extensibilidade. A definição acima para makeCopy torna a hierarquia inextensível, já que ela não conhece D, E, F etc.

O que "reflexão" permite adicionalmente (em Java e C#) não é realizar o teste acima, que C++ sempre permitiu, mas:
  1. Obter uma referência para um objeto especial "classe concreta" do referente de source;
  2. Obter uma referência para um objeto especial "construtor por cópia" a partir do objeto especial indicado em (1);
  3. Avaliar uma expressão "new" ou equivalente com o construtor indicado em (2) para criar um novo objeto.

Observe como o mecanismo acima não é suficiente para garantir uma implementação genérica de makeCopy: a possibilidade de enumerar construtores em (2) não garante a presença de um construtor por cópia na lista de construtores do objeto obtido em (1). Invariavelmente, é preciso um contrato entre programadores para garantir a presença do construtor por cópia. Porém, se é preciso pagar o custo deste contrato, i.e. impor ordem aos humanos programadores, a "reflexão" trás zero economia comparada com uma simples função virtual pura.

--

 P.

Thiago Adams

unread,
Feb 24, 2012, 8:06:41 AM2/24/12
to ccppbrasil


On Feb 24, 10:55 am, "P." <pedro.lama...@gmail.com> wrote:
> Em sexta-feira, 24 de fevereiro de 2012 10h09min42s UTC-2, ViniGodoy
> escreveu:
>
>
>
> > Nesse caso, também seriam exemplos de sobrecarga, certo? Pois você teria
> > que elaborar um construtor para cada tipo da classe filha.
>
> > O C++ não tem reflexão, portanto, não é possível (pelo menos, não de forma
> > trivial sem RTTI ou sem usar recursos mais macabros) testar pelo tipo do
> > objeto apontado pelo ponteiro.
>
> Eis como C++ testa o tipo de objeto apontado pelo ponteiro para implementar
> makeCopy (pouco) genérica.
>
> A* makeCopy (A* source)
> {
>     if (dynamic_cast<B*>(source) != nullptr) return makeCopyB(source);
>     else if (dynamic_cast<C*>(source) != nullptr) return makeCopyC(source);
>     else return nullptr;
>
> }

O dynamic_cast não diz o tipo do objeto.
Ele diz se o cast é possível. Quem diz o tipo
do objeto é o typeid. Então são coisas bem
diferentes.

A maneira não intrusiva de fazer isso é a seguinte:


A* clone(A* pA)
{
if (typeid(*pA) == typeid(A))
{
return new A(*dynamic_cast<A*>(pA));
}
else if (typeid(*pA) == typeid(B))
{
return new B(*dynamic_cast<B*>(pA));
}
else if (typeid(*pA) == typeid(C))
{
return new C(*dynamic_cast<C*>(pA));
}
return nullptr;
}

Thiago Adams

unread,
Feb 24, 2012, 8:09:07 AM2/24/12
to ccppbrasil

> O dynamic_cast não diz o tipo do objeto.
> Ele diz se o cast é possível. Quem diz o tipo
> do objeto é o typeid. Então são coisas bem
> diferentes.
>
> A maneira não intrusiva de fazer isso é a seguinte:
>
> A* clone(A* pA)
> {
>   if (typeid(*pA) == typeid(A))
>   {
>     return new A(*dynamic_cast<A*>(pA));
>   }
>   else if (typeid(*pA) == typeid(B))
>   {
>     return new B(*dynamic_cast<B*>(pA));
>   }
>   else if (typeid(*pA) == typeid(C))
>   {
>     return new C(*dynamic_cast<C*>(pA));
>   }
>   return nullptr;
>
> }

Nesta solução é importante que A seja polimorfico pois depende de RTTI
class A
{
public:
virtual ~A(){};
};

Уθя¡ςκ

unread,
Feb 24, 2012, 8:28:00 AM2/24/12
to ccppbrasil
Outra coisa importante nessa solução é não cair nessa armadilha:
http://www.ckode.dk/programming/staying-dry-mind-those-switch-statements/

Reuben Morais

unread,
Feb 24, 2012, 9:17:31 PM2/24/12
to ccppb...@googlegroups.com
Outra opção é usar tagged unions. Aqui tem uma boa introdução sobre
eles na implementação de strings no SpiderMonkey:
http://blog.mozilla.com/ejpbruel/2012/02/06/how-strings-are-implemented-in-spidermonkey-2/

-- reuben

Reply all
Reply to author
Forward
0 new messages