¿cómo acceder a un método de una clase DERIVADA a través de un puntero de una clase BASE?

publicado por: Anonymous

tengo este código que se ha vuelto un pequeño laberinto.
Tengo un array dinámico hecho de una clase BASE llamada LugarLaberinto
y tengo dos clases DERIVADAS que son muro y EspacioAbierto

Básicamente lo que quiero es leer un archivo de texto con un laberinto que puede tener dimensiones variables y leer carácter por carácter el archivo y recrear el laberinto dentro de un array de punteros

LugarLaberinto*** lugares;

pero la clase derivada EspacioAbierto tiene métodos que me permiten cambiar el espacio Abierto con por ejemplo la persona dentro del laberinto.

mi pregunta entonces es ¿cómo acceder a un método de la clase EspacioAbierto a través de un puntero de una clase LugarLaberinto?

y aqui les dejo el codigo con los comentarios. (trate de hacer el código lo mas reducido para mostrar la duda, pero no pude reducirlo mas que esto. Gracias)

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

//####################################
//clase BASE lugar del laberinto 
class LugarLaberinto
{
    public:
        virtual char mostrarCaracter()=0;
    protected:
        char caracter;
};


//####################################
//clase muro DERIVADA de lugar del laberinto 
class muro : public LugarLaberinto
{
    public: 
        muro();
        ~muro();
        char mostrarCaracter();
};

muro::muro()
{
    caracter = '#';
}

char muro::mostrarCaracter()
{
    return caracter;
}
//####################################
//clase espacioAbierto DERIVADA de lugar del laberinto 
class EspacioAbierto : public LugarLaberinto
{
    public:
        EspacioAbierto();
        ~EspacioAbierto();
        char mostrarCaracter();
        // funcion de la clase derivada
        void hayAlguien(bool);
};

EspacioAbierto::EspacioAbierto()
{
    caracter=' ';
}

char EspacioAbierto::mostrarCaracter()
{
    return caracter;
}

void EspacioAbierto::hayAlguien(bool)
{
    caracter='@';
}
//####################################
//clase laberinto
class laberinto
{
    public:
        //constructor que toma de un stream los valores del laberinto
        laberinto (std::ifstream&, int, int);
        ~laberinto();
        LugarLaberinto*** lugares;
        LugarLaberinto* obtenerLugar(int, int);
};

laberinto::laberinto(ifstream& fin, int alto, int ancho)
{
    char bloque;
    string auxiliar;

    lugares = new LugarLaberinto**[alto];
    //fin.ignore();
    for (int i=0; i< alto ; i++)
    {
        lugares[i] = new LugarLaberinto*[ancho];
        // leo una linea completa del archivo laberinto
        // y la guardo en auxiliar
        getline(fin,auxiliar);
        //cout << auxiliar;
        for (int j=0; j< ancho; j++)
        {
            //leo cada uno de los caracteres y los almaceno en la variable bloque
            bloque = auxiliar [j];
            // Basado en si es un # o si es un espacio basio creo el puntero 
            //hacia lugar de laberinto
            if (bloque=='#')
                this->lugares[i][j] = new muro();
            else 
            {
                this->lugares[i][j] = new EspacioAbierto();
                //########################
                //aqui esta mi duda
                // me gustaría acceder al metodo 
                // void hayAlguien(bool) 
                // a traves del puntero lugares
                // sin embargo no se si sea posible
                // o no se como hacerlo
                /*
                // aqui dice que la clase lugar laberinto no tiene
                //un miembro llamado hayAlguien y bueno eso lo sé
                // pero no quiero crear una funcion virtual para cada miembro
                // de esta clase derivada
                if (bloque=='@')
                    this->lugares[i][j]->hayAlguien(true);
                */
                // encontré esta manera de hacerlo pero no se si es la única
                // que es creando un nuevo objeto modificandolo y luego 
                // asignándolo, sin embargo no me siento totalmente cómodo
                // pensando que tengo que crear objetos a cada instante que 
                // quiera modificar el objeto.
                if (bloque=='@')
                {
                    EspacioAbierto *aux= new EspacioAbierto();
                    aux->hayAlguien(true);
                    lugares[i][j]=aux;

                }


            }

                //########################
            cout << this->lugares[i][j]->mostrarCaracter();
        }
        cout << endl;
    }

}

int main(int argc, char** argv) {

ifstream fin;
fin.open("laberinto.txt");
int alto = 10;
int ancho =10;
laberinto *nivel  =  new laberinto(fin, alto, ancho);
return 0;
}

para finalizar adjunto el archivo laberinto.txt

##########
#@#      #
# #####  #
#        #
######   #
#        #
#  #######
#   #    #
#       e#
##########

solución

Tienes 4 formas:

Aquí mando yo

Si estás absolutamente seguro de que tus punteros hacen referencia a las clases correctas, puedes utilizar un forzado de tipos. Usando estos 2 métodos, es tu responsabilidad el comprobar que un puntero a la clase base apunta realmente a una instancia de la clase hija.

Estas 2 formas fuerzan al compilador a aceptar sin rechistar lo que tu le indicas, por muy ilógica que sea la conversión.

  1. Al modo C.

Utiliza la expresión (clase_derivada *)puntero_a_clase_hija:

LugarLaberinto *ll;

(muro *)ll->mostrarCaracter( );

Esta sintaxis es heredada de C. No deberías usarla, puesto que resulta complicado buscarla entre las líneas de código si, por algún motivo, has de realizar algún cambio.

  1. Al modo C++.

Es similar al anterior, pero utilizamos la palabra reservada reinterpret_cast.

LugarLaberinto *ll;

reinterpret_cast< muro * >( ll )->mostrarCaracter( );

Esta sintaxis es mucho mas fácil de localizar; basta con realizar una búsqueda de reinterpret_cast en el texto, para encontrarla. Facilita cambios posteriores, y usa una sintaxis mas al estilo C++.

Soy bueno, pero por si acaso

  1. Utilizando static_cast< >( ).

Esto puede considerarse un método mas suave que los anteriores. No fuerza al compilador a admitir cualquier cosa; nos mostrará un error si los tipos implicados son claramente incompatibles.

LugaLaberinto *ll;

static_cast< muro * >( ll )->mostrarCaracter( );

C++ ¡ Ayudame !

Ya que estás en C++, puedes aprovecharte de ello y dejar que sea el runtime del lenguaje el que comprueba que una instancia pertenece a una clases heredera de otra.

Para aprovecharte de esto, las clases han de tener métodos virtuales, cosa que ya tienes hecha en LugarLaberinto.

  1. Utilizando dynamic_cast< >( ).

Similar a los anteriores:

LugaLaberinto *ll;
muro *m = dynamic_cast< muro * >( ll );

if( m ) {
  m->mostrarCaracter( );
  ...

Como ves, hacemos un uso algo diferente. El operador dynamic_cast devuelve un puntero si la conversión es posible, o NULL si la conversión no es posible (por ejemplo, porque el puntero original apunta a una clase no hija).

También, si lo utilizamos con referencias, en lugar de devolver NULL (cosa no posible con referencias), lanzará la excepción std::bad_cast si la conversión no es posible.

De las 4 formas posibles, dynamic_cast es la única con coste en tiempo de ejecución; todas las demás son realizadas en tiempo de compilación; está última sufre una mínima penalización, puesto que se realiza un comprobación sobre si los punteros a las clases son o no realmente compatibles (usando `la VTABLE; de ahí la necesidad de métodos virtuales).

Respondido por: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *