¿Recorte de Objetos?[Resuelto]

Iniciado por vangodp, 30 Marzo 2014, 17:33 PM

0 Miembros y 2 Visitantes están viendo este tema.

vangodp

Bueno Gracias de antemano si alguien sabe algo sobre eso.
El caso es que viendo un vídeo en youtube me di con una cosa que denominada slicyng según el dueño del vídeo(Outkast) es algo como recorte de funciones.
https://www.youtube.com/watch?v=_qS9YfxxdDw
Hice un código sencillo ya que como novato para mi es mejor así :D
Código (cpp) [Seleccionar]

#include <iostream>
using namespace std;

class base {
   public:
       base(){}
       ~base(){}
       virtual void comunicar() { cout << "BASE!" << endl; }        
};
class derivada: public base {
   public:
       derivada() {}
       ~derivada(){}
       void comunicar() { cout << "DERIVADA!" << endl; }  
};                                                        
void punte( base* c ){c->comunicar();}
void refer( base &c ){c.comunicar();}
void valor( base c  ){c.comunicar();}                        

int main (){      
   derivada *miclase = new derivada;    
   punte(  miclase );     //puntero
   refer( *miclase );    //referencia
   valor( *miclase );   //valor
   
   delete miclase;
   miclase = NULL;                      
   cin.ignore();
   return 0;
}


Según el o lo que entendí..a ver como lo explico rápido: En las funciones punte(), refer() y valor(), paso a ellas sus respectivos valores(punteros, referencia y valor) de una función creada en el heap llamada miclase que es la clase derivada.

¿Por que cuando llamo a las funciones, esas funciones esperan la clase base, sin embargo les paso la derivada y obtengo resultados de esta clase? Exceptuando cuando lo paso por valor ¡WTF!
O sea que puedo acceder a las clases heredadas desde las clases base si hago con puntero o referencia. :/
Menudo raleo me esta haciendo eso jejej
¿Eso es por la tal tabla virtual?
En fin a ver si es eso cierto por que ya tengo una stormbrain con eso de acceder por todas las partes a las clases jeje
Y eso que ha dicho que el gordo aun esta por venir XDD
Saludos ^^
Y gracias de antemano si alguien me puede explicar eso de forma mas sencilla. ;-)




eferion

Este ejercicio se basa en polimorfismo.

Cuando tu declaras un metodo como virtual y lo sobreescribes en una clase derivada, el compilador llamará automáticamente a la función de la derivada, aunque el puntero sea de la clase base.

En el caso de que pases la clase por valor se llama al constructor copia... y claro, el compilador no es tan listo y llama al constructor copia de la clase base. En este caso la clase base tiene su propia función y es la que acaba siendo llamada.

vangodp

Cita de: eferion en 30 Marzo 2014, 20:14 PM
En el caso de que pases la clase por valor se llama al constructor copia... y claro, el compilador no es tan listo y llama al constructor copia de la clase base. En este caso la clase base tiene su propia función y es la que acaba siendo llamada.
Gracias...Un poco mas claro. Pero aun tengo una duda..¿Entonces realmente esa función no pertenece a la clase derivada cuando paso por valor sino a la clase base?
El tema del polimorfismo me va quitar de vivir XDD
Gracias mas una vez...

eferion

A ver.

Si tu tienes

Código (cpp) [Seleccionar]

class base
{
    public:
        base(){}
        ~base(){}
        void comunicar() { cout << "BASE!" << endl; }       
};

int main (){       
    base *miclase = new base;
    miclase->comunicar( );
    return 0;
}


La situación es clara, la salida por pantalla será "BASE!".

Si ahora hacemos:

Código (cpp) [Seleccionar]

class base
{
    public:
        base(){}
        ~base(){}
        void comunicar() { cout << "BASE!" << endl; }       
};

class derivada: public base {
    public:
        derivada() {}
        ~derivada(){}
        void comunicar() { cout << "DERIVADA!" << endl; } 
};
int main (){       
    base *miclase = new derivada;
    miclase->comunicar( );
    return 0;
}


La salida seguirá siendo exactamente la misma, ya que al no ser virtual la función, al hacer el cast a la clase base la función llamada será la de la clase base. Dicho de otra forma, cada clase tendrá su propia función "comunicar", aunque se trate del mismo objeto:

Código (cpp) [Seleccionar]

    derivada *miclase1 = new derivada;
    base* miclase2 = miclase1;

    miclase1->comunicar( );
    miclase2->comunicar( );


Salida:

DERIVADA!
BASE!


Tercer ejemplo:

Código (cpp) [Seleccionar]

class base
{
    public:
        base(){}
        ~base(){}
        virtual void comunicar() { cout << "BASE!" << endl; }       
};

class derivada: public base {
    public:
        derivada() {}
        ~derivada(){}
        void comunicar() { cout << "DERIVADA!" << endl; } 
};
int main (){       
    base *miclase = new derivada;
    miclase->comunicar( );
    return 0;
}


Ahora la salida por pantalla cambia a "DERIVADA!". La razón es que, al crear una instancia de derivada, el compilador sustituye la función "comunicar" de base con la correspondiente de la clase "derivada". Al declarar la función como virtual se obliga al compilador a comprobar posibles sustituciones de la función.

Cuarto ejemplo:

Código (cpp) [Seleccionar]

#include <iostream>
using namespace std;

class base {
    public:
        base(){ }
        ~base(){}
        base( const base& otro ) { cout << "copia BASE!" << endl; }
        virtual void comunicar() { cout << "BASE!" << endl; }       
};
class derivada: public base {
    public:
        derivada() {}
        ~derivada(){}
        derivada( const derivada& otro ) { cout << "copia DERIVADA!" << endl; }
        void comunicar() { cout << "DERIVADA!" << endl; } 
};                                                       
void punte( base* c ){c->comunicar();}
void refer( base &c ){c.comunicar();}
void valor( base c  ){c.comunicar();}                       

int main (){       
    derivada *miclase = new derivada;   
    punte(  miclase );     //puntero
    refer( *miclase );    //referencia
    valor( *miclase );   //valor

    delete miclase;
    miclase = NULL;                     
    cin.ignore();
    return 0;
}


Salida:


DERIVADA!
DERIVADA!
copia BASE!
BASE!


Al pasar la clase por valor se hace necesario crear una copia local de la clase. Dado que el argumento de la función es de tipo "base", se realizará una copia de "base", es decir, la copia perderá la herencia a "derivada" . Como se puede ver en el código, la función "comunicar" de "base" imprime "BASE!", por lo que el código cumple con lo que se le pide.

vangodp

#4
Muchas gracias compañero, mas claro imposible.
Voy a estudiar el código con lupa
¡Que artista eres!  ;-)
Suerte (y)

Yes!! creo que lo pille ^^ jeje Gracias eferion. Entre tu explicación y el video creo que le pille el punto.
Con lo de la copia te refieres a eso "lo del recorte"....¿verdad?
A ver si me equivoco:
Como explicas en el argumento de la función se espera un objeto tipo base.Se "recorta" lo la parte derivada ¿Es eso? -_-'.
¿Eso es lo que hace el constructor de copia?Desecha todo lo que no esta esperando con la copia?

Lo del constructor de copia me lo tengo crudo.Voy a tener que repasar por que no lo capto :-(
¿Algún consejo?

Por cierto se me activo 2 cosas que no se que son :s una v y una x al lado de modificar mensaje.
¿Que es, alguien sabe??Vaya que la apriete y borre todo el post y me come con papas eternal XDD

eferion

Código (cpp) [Seleccionar]

class Base
{
  public:
    Base( )
    { std::cout << "Constructor BASE" << std::endl; }

    Base( const Base& otro )
    { std::cout << "Constructor copia BASE" << std::endl; }

    Base& operator=( const Base& otro )
    { std::cout << "Operador asignacion BASE" << std::endl; }
};

class Derivada : public Base
{
    Derivada( )
    { std::cout << "Constructor DERIVADA" << std::endl; }

    Derivada( const Derivada& otro )
    { std::cout << "Constructor copia DERIVADA" << std::endl; }

    Derivada& operator=( const Derivada& otro )
    { std::cout << "Operador asignacion DERIVADA" << std::endl; }
};

int main( )
{
  Derivada derivada; // Constructor por defecto
  std::cout << "---" << std::endl;
  Derivada derivada2( derivada ); // Constructor copia
  std::cout << "---" << std::endl;
  Derivada derivada3 = derivada; // Constructor copia
  std::cout << "---" << std::endl;
  Derivada derivada4 = Derivada( ); // Constructor por defecto
  derivada4 = derivada2; // Operador de asignacion
  std::cout << "---" << std::endl;
  Base base( derivada ); // Constructor copia

  Base* base2 = new Base( derivada ); // Constructor copia
  delete base2;

  return 0;
}


El constructor copia permite, como su nombre indica, crear una réplica del objeto original. Parece un tema trivial y sencillo. Lo que sucede es que con herencia la cosa se complica.

Los constructores y operadores, como habrás podido comprobar con el código anterior, no son heredables, es decir, si tu llamas al constructor copia de "Derivada", no se llama por defecto al constructor copia de "Base", sino a su constructor por defecto... y al utilizar el operador de asignación de "Derivada" no se llama al operador de asignación de "Base". Este mecanismo impide al código crear una copia completa de un objeto si la clase destino se corresponde con una de las clases padres de la clase original ( en el ejemplo tenemos una instancia de "Derivada" y la copiamos dentro de una clase "Base"... el resultado es que se copia solo la parte correspondiente a "Base" y se ignora el resto.

Es un efecto colateral del polimorfismo. Por eso, en entornos polimórficos se hace necesario utilizar un mecanismo diferente para realizar copias exactas de los objetos sin importar su tipo concreto. Me refiero a los patrones de clonación:

Código (cpp) [Seleccionar]

class Base
{
  public:
    Base( )
    { std::cout << "Constructor BASE" << std::endl; }

    Base( const Base& otro )
    { std::cout << "Constructor copia BASE" << std::endl; }

    Base& operator=( const Base& otro )
    { std::cout << "Operador asignacion BASE" << std::endl; }

    virtual Base* Clone( )
    { return new Base( *this ); }

    virtual void Comentar( )
    {std::cout << "Clase BASE" << std::endl;
};

class Derivada : public Base
{
    Derivada( )
    { std::cout << "Constructor DERIVADA" << std::endl; }

    Derivada( const Derivada& otro )
    { std::cout << "Constructor copia DERIVADA" << std::endl; }

    Derivada& operator=( const Derivada& otro )
    { std::cout << "Operador asignacion DERIVADA" << std::endl; }

    virtual Base* Clone( )
    { return new Derivada( *this ); }

    virtual void Comentar( )
    {std::cout << "Clase DERIVADA" << std::endl;
};

int main( )
{
  Base* base = new Derivada( );
  Base* base1 = base->Clone( );
  Base* base2 = new Base( *derivada );

  base1->Comentar( );
  base2->Comentar( );

  delete base;
  delete base1;
  delete base2;

  return 0;
}


Como se puede ver en el código anterior, al llamar al constructor copia de "Base", se crea invoca al constructor copia de "Base", y no al de "Derivada", por lo que el objeto final será de tipo "Base". Sin embargo, al llamar a "Clone", como éste es un método virtual, se llama a la versión de "Derivada", que sí que es capaz de crear un objeto copia de éste tipo.

Espero que con esto te haya terminado de resolver las dudas. Si no es así, sigue preguntando ;)

Un saludo.

vangodp

#6
Eres una enciclopedia ambulante =D
Que buenos los ejemplos. Sencilos lo justo para pillar el tema, ni los que enseñan lo hacen así, te lo enseñan 40 códigos y terminas por no pillar ninguno jeje.
Tan buenos son que me hacen ver que me salte alguna aula XDDD
Código (cpp) [Seleccionar]
Base& operator=( const Base& otro )
      { std::cout << "Operador asignacion BASE" << std::endl; }

No tengo ni idea sobre esto... jeje
voy a tener que ponerme al día con algunas cosas... me parece que no tengo ni idea sobre algunos temas y mejor los repaso otra vez por que se ve que tengo carencias.
Por lo demás solo puedo decir gracias y mas mil gracias =)
Aprender C++por cuenta cuesta mucho -_-'
Si no soys vosotros... pffff
No me olvidare la ayuda.Graciasssss  ;-) ;-) ;-)

Doy el tema por terminado.

En la linea 42 del ultimo ejercicio:
  Base* base2 = new Base( *derivada );
Seria esto:
Base * base2 = new Base ( *base );
¿no?
por que "derivada" no esta declarada y Derivada no sera :D

eferion

Cita de: vangodp en 31 Marzo 2014, 20:44 PM
En la linea 42 del ultimo ejercicio:
  Base* base2 = new Base( *derivada );
Seria esto:
Base * base2 = new Base ( *base );
¿no?
por que "derivada" no esta declarada y Derivada no sera :D

Cierto, cosas del copypaste... escribí todo sobre la marcha, desde el movil no se puede hacer mucho mas.

Cita de: vangodp en 31 Marzo 2014, 20:44 PM
Eres una enciclopedia ambulante =D

Muchas gracias por el cumplido, pero siendo sincero, aquí hay gente tanto o más buena que yo. Ayudo en lo que puedo.

Cita de: vangodp en 31 Marzo 2014, 20:44 PM
No tengo ni idea sobre esto... jeje

Es una sobrecarga del operador de asignación. Por defecto C++ asigna un operador de asignación por defecto, pero te ofrece la posibilidad de sobrecargarlo para adaptarlo a tus necesidades... por ejemplo por si manejas memoria dinámica, no copiar los punteros en sí sino también su contenido (para que cada copia tenga su propia memoria). De la misma forma se pueden sobrecargar el resto de operadores, incluso para los usos más variopintos e insospechados.

Cita de: vangodp en 31 Marzo 2014, 20:44 PM
Aprender C++por cuenta cuesta mucho -_-'

Yo lo dejaría en "Aprender C++ cuesta mucho". C++ es un lenguaje muy abierto con infinitas posibilidades y formas de hacer las cosas y eso hace que cueste muchísimo sentir que lo dominas en su mayoría ( completamente ya te digo que es una tarea compleja, tiene partes, como los templates... hay gente que hace maravillas con eso aunque yo no soy partidario de escribir código tan enrevesado. ). Pero bueno, al final te haces con el timón y eso te hace sentir orgulloso de ti mismo XD

Y poco más, si te aparecen más dudas tu pregunta, que para eso estamos.