[c++] Duvida com cast, polimorfismo

6 respostas
G

Boa tarde, pessoal, tudo bom?

Estou com um problema e espero que possam me ajudar. Tenho um vetor da classe A, usei struct neste exemplo. E uma struct filho que herda A. Depois criei um vetor de objetos A, dai gostaria de pegar um desses elementos do vetor e fazer um cast para B, sentiram o drama? Tenho um pequeno exemplo para facilitar o entendimento.

#include "stdafx.h"
#include <algorithm>
#include <iostream>
#include <functional>
#include <vector>

struct A {
    virtual void foo() {
        std::cout << "A::foo()" << std::endl;
    }
};
struct B: public A {
    virtual void foo() {
        std::cout << "B::foo()" << std::endl;
    }
};

int main()
{
    std::vector<A*> VectorOfAPointers;
    A *test = new A();
    VectorOfAPointers.push_back(new B());
    //VectorOfAPointers.push_back( (B) test);
    std::for_each(VectorOfAPointers.begin(), VectorOfAPointers.end(), std::mem_fun(&A::foo));
    return 0;
}

Achei o num site, é muito similar ao que preciso, porém ele não faz cast, apenas adiciona um elemento B ao vector A, é quase oque preciso, o erro acontece na linha comentada, gostaria de inserir um elemento B neste vetor A sem dar um new. É possivel?

Espero que possam me ajudar.

Valeu.

PS: Esqueci de arrumar a linha comentada: o correto seria VectorOfAPointers.push_back( (B*) test);, com ponteiro, dai quando executo o programa o metodo que eu chamei eh o de A nao o de B que eu utilizei o cast. Esse é o meu problema.

6 Respostas

T

Não se pode usar polimorfismo em C++ sem usar ponteiros (tanto usando *, como você deve ter visto, como usando shared_ptr).

Isso é uma restrição da linguagem.

Aprenda a usar shared_ptr; ele é quase como se fosse uma referência do Java.

O shared_ptr está disponível no C++0X (que está parcialmente implementado no Visual Studio 2010 e nas versões mais recentes do g++) e também na biblioteca boost.

Vou dar o mesmo exemplo, mas usando a biblioteca boost.

#include "stdafx.h"  
#include &lt;algorithm&gt;  
#include &lt;iostream&gt;  
#include &lt;functional&gt;  
#include &lt;vector&gt;  
#include &lt;boost/shared_ptr.hpp&gt;

using namespace boost;

// Amigo, evite usar struct em C++, quando você quer dizer "class". 
// Não economize no uso da palavra-chave "public:".
  
class A {  
public:
     virtual void foo() {  
          std::cout &lt;&lt; "A::foo()" &lt;&lt; std::endl;  
     }  
     virtual int bar() { return 1; }
     A() {
          std::cout &lt;&lt; "Construtor de A chamado" &lt;&lt; std::endl;
     }
     virtual ~A() {
          std::cout &lt;&lt; "Destrutor de A chamado" &lt;&lt; std::endl;
     }
};  
class B: public A {  
public:
     virtual void foo() {  
          std::cout &lt;&lt; "B::foo()" &lt;&lt; std::endl;  
     }  
     virtual int bar() { return 2; }
     B() {
          std::cout &lt;&lt; "Construtor de B chamado" &lt;&lt; std::endl;
     }
     virtual ~B() {
          std::cout &lt;&lt; "Destrutor de B chamado" &lt;&lt; std::endl;
     }
};  

int main(int argc, char* argv[])  
{  
    typedef std::vector&lt;shared_ptr &lt; A &gt; &gt; VectorOfAPointers;  
    VectorOfAPointers v;
    v.push_back(shared_ptr<A> (new A()));
    v.push_back(shared_ptr<A> (new B()));  
    for (VectorOfAPointers::iterator it = v.begin(); it != v.end(); ++it) {
        (*it)-&gt;foo();
    }
    std::cout &lt;&lt; v[0]-&gt;bar() &lt;&lt; std::endl;
    std::cout &lt;&lt; v[1]-&gt;bar() &lt;&lt; std::endl;
    return 0;  
}

Rode o programa acima (não se esqueça de incluir a biblioteca boost, em http://www.boost.org ).

O programa, ao ser executado, imprime:

Construtor de A chamado
Construtor de A chamado
Construtor de B chamado
A::foo()
B::foo()
1
2
Destrutor de A chamado
Destrutor de B chamado
Destrutor de A chamado

Explique por que:
a) Você não precisa chamar "delete" explicitamente, ao usar um shared_ptr.
b) O construtor de A é chamado duas vezes. (Dica: o construtor de B é implementado em termos do construtor de A, se não for explicitamente declarado).
c) O destrutor de A é chamado duas vezes. (Mesma coisa)

G

Olá Thingol, primeiro, muito obrigado pela explicação e conselho. Não conhecia shared_ptr e do fato de ser necessário utilizar obrigatoriamente ponteiros quando preciso usar polimorfismo. Meu código é bastante extenso, este pequeno exemplo era somente para ilustrar a parte onde tenho problemas.
Não descarto a possibilidade de utilizar shared_ptr, porém em último caso, estamos tentando usar poucas bibliotecas no projeto, para termos mais controle, porém acredito que boost não deve haver problemas, precisaria discutir isso com a equipe.

Vou baixar o boost e tentar fazer o que preciso neste seu exemplo, precisaria adicionar um componente ao vector que não seja criado na hora, igual vc fez na linha 45. Preciso que seja um objeto A criado anteriormente, depois seja feito cast para B e por fim adicionado ao vector, eu consigo fazer isso porém o método foo chamado é da classe A não da B, editei meu topico para ficar mais claro :slight_smile:

Nao sei se vai funcionar com boost, vou testar, posto aqui se conseguir :slight_smile:

Abraços e obrigado.

T

Estamos fazendo um projeto gigantesco aqui em C++ e basicamente só usamos três bibliotecas de terceiros:

xerces - para analisar arquivos XML;

sqlite - para os casos em que precisamos de bases próprias de dados;

boost - para todo o resto (ponteiros, sockets, expressões regulares, estruturas de dados complexas, threads, semáforos e mutexes, etc.)

Se você souber usar corretamente o shared_ptr, vai ver que vários de seus problemas com alocação de memória vão magicamente se resolver. Aqui raramente temos problemas com vazamentos de memória ou ponteiros destruindo outras áreas da memória.*

O “multi_index” do Boost é como se fosse uma tabela com vários índices em memória. Chega de brigar com “map” e “multimap”. O multi_index tem todas as funcionalidades do map, e muitas mais.

  • Só ocorre isso quando chega um novato ao projeto que teima em programar do jeito “antigo”; no primeiro vazamento de memória que ocorre a gente força o cara a usar o Boost para tudo.
G

Olá Thingol, blz?

Baixei e estou testando, logicamente vou precisar de um tutorial, mas inicialmente gostaria de verificar se ele consegue resolver meu problema, usei seu codigo de exemplo, mas adicionei 2 linhas no main:

A *test = new A();
    v.push_back(shared_ptr<A> (new A()));
    v.push_back(shared_ptr<A> (new B()));  
    v.push_back(shared_ptr<A> ((B*) test));

Também testei:

v.push_back(shared_ptr<B> ((B*) test));

Infelizmente, em ambos os casos o metodo foo chamado pela terceira posicao do vetoc v é o metodo foo do objeto A e não o foo do objeto B, saída do sistema fica:
A::foo()
B::foo()
A::foo()

Preciso de um
A::foo()
B::foo()
B::foo()

Você teria alguma sugestão?

Valeu.

E

Converter um A em B desse jeito (só mudando o tipo do ponteiro) não faz sentido nenhum.
Não é porque você dá o nome de “engenheiro de logística” ao carregador de malas do aeroporto que ele vai saber engenharia de logística.
Você pode criar um construtor de B que recebe um A (para fazer a conversão), mas aí você vai criar um B, não um A.

G

Já tinha começado a pensar nisso.

Valeu pela resposta, Edson.

Até a próxima.

Criado 25 de agosto de 2009
Ultima resposta 27 de ago. de 2009
Respostas 6
Participantes 3