FILECERU


 
ÍndiceÍndice  PortalPortal  FAQFAQ  BuscarBuscar  MiembrosMiembros  Grupos de UsuariosGrupos de Usuarios  RegistrarseRegistrarse  ConectarseConectarse  DescargasDescargas  

Comparte | 
 

 Objetos y Clases en c++

Ver el tema anterior Ver el tema siguiente Ir abajo 
AutorMensaje
Admin
Fundador
Fundador


Cantidad de envíos : 137
Puntos : 29183
Reputación : 13
Fecha de inscripción : 07/06/2009

<Hoja de personaje
Puntaje:
390/2000  (390/2000)
Ver perfil de usuario http://fileceru.forosactivos.net

MensajeTema: Objetos y Clases en c++   Mar Abr 20, 2010 9:37 am

Clases y Objetos

Este capitulo introduce a las clases en C++. La clase es la fundación de C++ para el soporte de la programación orientada a objetos, y se encuentra en el núcleo de muchas de sus más avanzadas características. La clase es la unidad básica de C++ de la encapsulación y esta provee el mecanismo por el cual los objetos son creados.

Fundamentos de Clases

Vamos a comenzar definiendo los términos de clase y objeto. Una clase define un nuevo tipo de dato que especifica la forma de un objeto. Una clase incluye los datos y el código que operarán sobre esos datos. Además, una clase enlaza datos y código. C++ usa una especificación de una clase para construir objetos. Los objetos son instancias de una clase. Además, una clase es esencialmente un serie de planes que especifican cómo construir un objeto. Es importante estar claro en algo: Una clase es una abstracción lógica.

No es hasta que un objeto de esa clase sea creado que la representación física de la clase existe en la memoria. Cuando se define una clase, se declaran los datos que ésta contiene y el código que opera en esos datos. Aunque clases muy simples pueden contener solo código o solo datos, la mayoría de las clases en realidad contienen ambos. En conjunto con una clase, los datos se almacenan en las variables y el código en las funciones. Colectivamente, las funciones y variables que constituyen una clase son llamados 'miembros' de la clase. Una variable declarada dentro de una clase es llamada 'variable miembro', y una función declarada en una clase es llamada 'función miembro'. En ocasiones el termino 'variable de instancia' es usado en lugar de variable miembro.

Una clase es creada con la palabra clave class. La declaración de una clase es similar sintácticamente a una estructura ( y tienen muchísimo que ver ). Aqui tenemos un ejemplo. La siguente clase define un tipo llamado CRender, el cual es usado para implementar operaciones de rendereado en este caso.

Código:
// Esto crea la clase CRender
class CRender {
   char buffer[256];
public:
   void m_Renderear();
};

Veamos mas de cerca esta declaración de la clase.

Todos los miembros de CRender son declarados dentro de la declaración 'class'. La variables miembro de CRender es buffer. La función miembro es m_Renderear.

NOTA: Por defecto los miembros de una clase son privados.

Una clase puede contener miembros privados asi como públicos. Por defecto, todos los elementos definidos en una clase son privados. Por ejemplo la variable buffer es privada. Esto significa que solo puede ser accesada por otros miembros de la clase CRender, y no por alguna otra parte del programa. Esta es una forma de como la encapsulación se logra, se puede controlar el acceso a ciertos elementos de datos manteniendolos privados. Aunque no hay ninguna en este ejemplo, se pueden definir funciones privadas, las cuales pueden ser llamadas solamente por otros miembros de la clase.

Para hacer pública una parte de la clase ( accesible a otras partes del programa ) se deben declarar con la palabra clave public. Todas las variables o funciones definidas después de la declaración pública son accesibles por todas las demas funciones en el programa. En nuestra clase CRender, la función m_Renderear() es pública. Tipicamente, su programa accesará los miembros privados de una clase a través de sus funciones públicas. Note que la palabra clave public es seguida con : . Mantenga en mente que un objeto forma una relación entre código y datos. Una función miembro tiene acceso a los elementos privados de su clase. Esto significa que m_Renderear tiene acceso a buffer en nuestro ejemplo. Para añadir una función miembro a la clase, debe espcificar su prototipo en la definición de la misma.

Una vez que se ha definido una clase, se puede crear un objeto de ese tipo usando el nombre de la clase. El nombre de la clase se convierte en un especificador del nuevo tipo. Por ejemplo la siguiente declaración crea 2 objetos llamados render1 y render2 del tipo CRender.

Citación :
CRender render1, render2;

Cuando un objeto de la clase es creado, este tendra su propia copia de las variables miembros que contiene la clase. Esto significa que render1 y render2 tendrán su propia y separado copia de buffer. Además los datos asociados con render1 son distintos y separados de los datos asociados con render2.

Recordemos: En c++, una clase es un nuevo tipo de dato que puede ser usado para crear objetos. Especificamente, una clase crea una consistencia lógica que define una relación entre sus miembros. Cuando se declara una variable de una clase, se está creando un objeto. Un objeto tiene existencia física, y es una instancia específica de una clase. ( Esto es, un objeto ocupa espacio de memoria, pero una definición de tipo no ). Además, cada objeto de una clase tiene su propia copia de los datos definidos dentro de esa clase.

Dentro de la declaración de CRender, el prototipo de una función es especificado. Ya que las funciones miembros son prototipadas dentro de la definicion de la clase, no necesitan ser prototipadas en otro lugar cualquiera.

Para implementar una función que es un miembro de una clase, debe indicarle al compilador a cual clase pertenece la función calificando el nombre de la función con el nombre de la clase. Por ejemplo, esta es una manera de codificar la función m_Renderear().
Código:

void CRender::m_Renderear()
{
   strcpy(buffer, "C++ en wikibooks");
   return;
}

Resolución de ámbito

El :: es llamado el operador de resolución de ámbito. Esencialmente le dice al compilador que esta versión de m_Renderear pertenece a la clase CRender. O mirando diferente, :: declara que m_Renderear se encuentra en el ámbito de CRender. varias clases diferentes pueden usar los mismos nombres de función. EL compilador sabe cuál función pertenece a cual clase y esto es posible por el operador de resolución de ámbito y el nombre de la clase.
[editar] Acceso a la funciones

Las funciones miembros de una clase solo pueden ser llamadas relativas a un objeto específico. Para llamar a una función miembro desde alguna parte del programa que se encuentre fuera de la clase, se debe usar el nombre del objeto y el operador de direcionamiento '.' ( punto ). Por ejemplo, lo siguiente llama a m_Renderear() en el objeto objeto1.

Citación :
CRender objeto1, objeto2;

objeto1.m_Renderear();
La invocación de objeto1.m_Renderear() causa a m_Renderear() operar en los datos de la copia de objeto1. Mantenga en mente que objeto1 y objeto2 son 2 objetos separados. Esto significa, por ejemplo, que inicializar objeto1 no causa que objeto2 sea inicializado, La única relación que objeto1 tiene con objeto2 es que es un objeto del mismo tipo.

Cuando una función miembro llama a otra función miembro de la misma clase, puede hacerlo directamente, sin usar un objeto y el operador '.' En este caso, el compilador ya conoce en cuál objeto se está operando. Solamente cuando una función miembro es llamada por código que se encuentra fuera de la clase es cuando debe utilizarse el nombre del objeto y el operador '.' Por la misma razón, una función miembro puede referirse directamente a una variable miembro, pero código fuera de la clase debe referenciarse a la variable a través de un objeto y el operador '.'

El programa siguiente muestra aquí todas las piezas juntas y detalles perdidos, e ilustra la clase CRender.
[editar] Ejemplo

Código:
// Programa OPP01.CPP
#include <iostream>
 
using std::cout;
using std::endl;
 
 
// Esto crea la clase CRender
class CRender {
public:
    char buffer[256];
    void m_Renderear(const char *cadena);
};
 
 
/* implementar m_Renderear() para la c;*/
void CRender::m_Renderear(const char *cadena)
{
    strcpy(buffer, cadena);//copia la cadena
    return;
}
 
 
int main (int argc, char **argv)
{
    // crear 2 objetos CRender
    CRender render1, render2;
 
    render1.m_Renderear("Inicializando el objeto render1");
    render2.m_Renderear("Inicializando el objeto render2");   
 
    cout << "buffer en render1: ";
    cout << render1.buffer << endl;  // tenemos acceso a buffer ya que es publico.
 
    cout << "buffer en render2: ";
    cout << render2.buffer << endl;
 
return (0);
}

Este programa imprime:

buffer en render1: Inicializando el objeto render1 buffer en render2: Inicializando el objeto render2

Miembros de una clase ( métodos y atributos )

En el lenguaje coloquial de la programación orientada al objeto es común escuchar términos tales como: métodos, atributos, herencia, polimorfismo, etc. En esta sección nos encargaremos de hablar de los dos primeros.

Métodos:

En comparación con la programación tradicional, un método es lo mismo que una función cualquiera, salvo que como los métodos se declaran para pertenecer a una clase específica, se dice que todos los métodos de dicha clase son miembros de la misma. Por lo demás, la declaración y definición de los métodos es exactamente igual que declarar y definir cualquier otra función.

Atributos:

En comparación con la programación tradicional, un atributo es lo mismo que una variable cualquiera, salvo que como los atributos se declaran para pertenecer a una clase específica, se dice que todos los atributos de dicha clase son miembros de la misma. Por lo demás, la declaración de los atributos es exactamente igual que declarar cualquier otra variable.


Miembros:

A partir de este momento usaremos la palabra miembro para referirnos al hecho de que un método o un atributo pertenece a tal o cual clase.

Por Ejemplo, en el programa OOP01.CPP ( visto anteriormente ) la Clase CRender posee dos miembros, buffer que es un atributo; y m_Renderear que es un método.

Citación :
class CRender {
public:
char buffer[256]; // atributo
void m_Renderear(const char *cadena); // método
};
Volver arriba Ir abajo
Admin
Fundador
Fundador


Cantidad de envíos : 137
Puntos : 29183
Reputación : 13
Fecha de inscripción : 07/06/2009

<Hoja de personaje
Puntaje:
390/2000  (390/2000)
Ver perfil de usuario http://fileceru.forosactivos.net

MensajeTema: Re: Objetos y Clases en c++   Mar Abr 20, 2010 9:44 am

Visibilidad de los miembros de una clase

Por visibilidad se entiende al acto de acceder a los miembros de una clase. En este sentido, los miembros de una clase pueden ser: públicos, privados y protegidos.

* Un miembro público significa que el acceso al mismo puede darse dentro del interior de la clase, dentro de una subclase, y desde un objeto instanciado de cualquiera de estas. Por ejemplo, los miembros de la clase CRender son accesibles dentro de la misma y podrán accederse desde cualquier otra clase que se derive de CRender, asi como desde cualquier objeto instanciado de estas.

* Un miembro privado significa que el acceso al mismo puede darse solamente dentro del interior de la clase que lo posee. Normalmente, el programador creador de una clase declara a los atributos de la clase como privados y a los métodos como públicos, esto con la idea de que el usuario de la clase no pueda tener acceso a los atributos sino es a travez de los métodos definidos para el caso.

* Un miembro protegido se comporta de manera parecida a un miembro privado, salvo que estos son accesibles dentro de la clase que lo posee y desde las clases derivadas, pero no desde los objetos instanciados a raiz de dichas clases.

Nota: por defecto, los miembros de una clase son privados.


En la clase Pareja que se verá en seguida, se declaran dos atributos y cuatro métodos para la manipulación de dichos atributos. Observe que los atributos son privados( por defecto ), mientras que los métodos se declaran públicos.

Código:
class Pareja
{
    // atributos
    double a, b;
 
public:
    // métodos
    double getA();
    double getB();   
    void  setA(double n);
    void  setB(double n);
};
 
// implementación de los métodos de la clase Pareja
//
double Pareja::getA() { return a; }
double Pareja::getB() { return b; }
void  Pareja::setA(double n) { a = n; }
void  Pareja::setB(double n) { b = n; }


Subclases

Una subclase es una clase que se deriva de otra. La clase que sirve de base suele conocerse como parent (padre), y a la subclase se le llama child (hija). En C++ cada clase que es creada se convierte en candidata para servir de base de donde se deriven otras. Por ejemplo, la clase Pareja es candidata para convertirse en la base para las subclases Suma, Resta, Multiplica, Divide, y otras posibles subclases en donde se utilice un par de valores numéricos.

Para poner un ejemplo, pensemos en que deseamos crear la clase Suma, misma que será utilizada para obtener la suma de dos números. Puesto que la clase Pareja posee dos atributos númericos puede ser usada como base para la clase que estamos proyectando. Así, el siguiente ejemplo se constituye en un caso de clases derivadas.

Nota: Observe que la sintaxis para crear una subclase es:

Citación :
class hija : [public | private] padre
{
...
};

Donde padre es la clase base e hija es la subclase.
Código:

class Suma : public Pareja
{
    // atributos de Suma
    double resultado;
 
public:
    // métodos de Suma
    double calcular();
};
 
// implementación de Suma
//
double Suma::calcular() { return getA() + getB(); }


Probando las clases Pareja y Suma

// programa OOP02.CPP
// Este programa pone a prueba el uso de las clase Suma,
// misma que es una subclase de la clase Pareja ( ambas definidas anteriormente).

Código:
#include <iostream.h>
 
int main()
{
    Suma s;
    s.setA(80);
    s.setB(100);
    cout << s.getA() << " + " << s.getB() << " = " << s.calcular() << endl;
    cin.get();
    return 0;
}



Herencia

La herencia es uno de los mecanismos más útiles de la programación orientada al objeto, ya que por medio de la misma se puede llevar a cabo la reutilización de código. Es decir, puesto que toda clase definida se convierte en candidata para ser usada como base de donde se deriven otras, esto da como resultado que las clases derivadas hereden todos los miembros de la clase base. Por ejemplo, la clase Suma vista en la sección anterior, hereda todos los miembros de la clase Pareja puesto que Suma es una extensión de Pareja. En ese sentido, podemos decir que existen dos tipos de herencia, por extensión y por agregación o composición. En el caso de las clases Pareja y Suma, se dice que Suma es una extensión de Pareja. Vista gráficamente, la herencia por extensión se puede representar asi:

Herencia por extensión



Al tipo de diagrama mostrado arriba (Herencia por extensión) se le conoce como UML [1] y es utilizado para mostrar de forma grafica la relación existente entre una clase hija con la clase padre. En el caso del ejemplo, se muestra que la clase Suma es una extensión de la clase Pareja y, en consecuencia, Suma posee a los miembros { a, b, getA(), getB(), setA(), setB() } heredados de la clase Pareja. Observe como la clase Suma posee otros dos miembros no heredados, { resultado, y calcular() }, y es precisamente a este tipo de situación por lo que se dice que Suma es una extensión de Pareja, ya que Suma, además de poseer a todos los miembros de Pareja, se extiende para poseer otros dos miembros.

Agregacion o composición

La composición se da en los casos en donde una clase posee un objeto que es una instancia de otra clase. Por ejemplo, la clase Suma podría escribirse de la siguiente forma:
Código:

class Suma
{
    // atributo privado   
    double resultado;
 
public:
    // método público
    double calcular();
 
    // atributo público
    Pareja  p;
};
 
// implementación de Suma
//
double Suma::calcular() { return p.getA() + p.getB(); }

Luego, si usted presta atención, notará que el miembro p de la clase Suma es un objeto o instancia de la clase Pareja, en consecuencia, la clase Suma puede acceder a los miembros de la clase Pareja a través de la variable p.
También se debe observar que la implementación del método calcular() es diferente que el mismo de la clase Suma original. Si usted desea poner a prueba a la nueva clase Suma, compile y ejecute el siguiente programa.


Código:
// programa OOP03.CPP
 
#include <iostream.h>
 
int main()
{
    Suma s;
    s.p.setA(80);
    s.p.setB(100);
    cout << s.p.getA() << " + " << s.p.getB() << " = " << s.calcular() << endl;
    cin.get();
    return 0;
}

Constructores

Un constructor es un método que pertenece a una clase y el mismo (en C++) debe tener el mismo nombre de la clase a la que pertenece. A diferencia de los otros métodos de la clase, un constructor deberá ser del tipo void, es decir, el mismo no regresará valor alguno. Una clase puede tener más de un método constructor. Cada clase debe tener al menos un constructor, tanto así que si el programador creador de una clase no define un método constructor para la misma, el sistema, o sea el compilador, creará de manera automática un constructor nulo.

El objetivo principal del constructor es el de establecer las condiciones necesarias dentro de la memoria y crear una copia del objeto mismo dentro de la memoria.

Los consturctores suelen usarse para la inicialización de los atributos de los objetos instanciados. Por ejemplo, con las instrucciones:

Citación :
Suma s;
s.setA(80);
s.setB(100);

del programa OOP02.CPP, se declara el objeto s de la clase Suma y luego se inicializan los atributos del objeto por medio de los métodos setA() y setB(). En este caso, es necesario hacer uso de dichos métodos debido al hecho de que no se ha definido un constructor para la clase Suma. Ahora bien, para evitar este tipo de situaciones podemos definir un método constructor para Suma y que este se encargue de inicializar los atributos. Veamos.

Notas: ya que Suma es una extensión de Pareja, se ha definido el metodo constructor de Pareja y este a la vez es invocado por el constructor de Suma. Otro punto de interes es el hecho de la definición de un constructor base para Pareja, ya que de acuerdo con las reglas de programación en C++ toda clase en donde se defina un constructor parametrizado deberá definir un constructor base, este regla se aplica en los casos en donde la clase proyectada servirá de base para otras.

Código:

// programa OOP04.CPP
// Ejemplo: clases Pareja y Suma, ambas con constructor
 
#include <iostream.h>
 
//------------------------
class Pareja
{
    // atributos
    double a, b;
 
public:
    // constructor de base ( null )
    Pareja() {}
 
    // constructror parametrizado
    Pareja(double x, double y) : a(x), b(y) {}
 
    // métodos
    double getA();
    double getB();
    void  setA(double n);
    void  setB(double n);
};
 
// implementación de los métodos de la clase Pareja
//
double Pareja::getA() { return a; }
double Pareja::getB() { return b; }
void  Pareja::setA(double n) { a = n; }
void  Pareja::setB(double n) { b = n; }
 
//------------------------
class Suma : public Pareja
{
    // atributos de Suma
    double resultado;
 
public:
    // constructor
    Suma(double a, double b) : Pareja(a, b) {}
 
    // métodos de Suma
    double calcular();
};
 
// implementación de Suma
//
double Suma::calcular() { return getA() + getB(); }
 
 
//------------------------
int main()
{
    Suma s(80, 100);
    cout << s.getA() << " + " << s.getB() << " = " << s.calcular() << endl;
    cin.get();
    return 0;
}


Destructores

Un destructor es un método que pertenece a una clase y el mismo (en C++) debe tener el mismo nombre de la clase a la que pertenece. A diferencia de los otros métodos de la clase, un destructor deberá ser del tipo void, es decir, el mismo no regresará valor alguno. Para diferenciar a un método destructor de un método constructor, al nombre del destructor se le debe anteponer el caracter ~ (Alt + 126).

El objetivo principal del destructor es el de retirar de la memoria al objeto, o sea, el destructor hace todo lo contrario que el constructor.

Los destructores suelen usarse para liberar memoria que haya sido solicitada por el objeto a travez de las ordenes malloc(), new, etc. En tales casos se deberá incluir dentro del método destructor la orden free, delete, etc., según sea el caso.

Código:

// clase Pareja con constructor y destructor
class Pareja
{
    // atributos
    double a, b;
 
public:
    // constructor de base ( nulo )
    Pareja() {}
 
    // constructror parametrizado
    Pareja(double x, double y) : a(x), b(y) {}
 
    // destructor
    ~Pareja() {}
 
    // métodos
    double getA();
    double getB();
    void  setA(double n);
    void  setB(double n);
};
Volver arriba Ir abajo
Admin
Fundador
Fundador


Cantidad de envíos : 137
Puntos : 29183
Reputación : 13
Fecha de inscripción : 07/06/2009

<Hoja de personaje
Puntaje:
390/2000  (390/2000)
Ver perfil de usuario http://fileceru.forosactivos.net

MensajeTema: Re: Objetos y Clases en c++   Mar Abr 20, 2010 9:58 am

Derivación múltiple

^

C++ permite crear clases derivadas a partir de varias clases base. Este proceso se conoce como derivación múltiple. Los objetos creados a partir de las clases así obtenidas, heredarán los datos y funciones de todas las clases base.

Sintaxis:

Citación :
() :
()
[, ()] {}

Pero esto puede producir algunos problemas. En ocasiones puede suceder que en las dos clases base exista una función con el mismo nombre. Esto crea una ambigüedad cuando se invoca a una de esas funciones.

Veamos un ejemplo:
Código:

#include <iostream>
using namespace std;

class ClaseA {
  public:
    ClaseA() : valorA(10) {}
    int LeerValor() const { return valorA; }
  protected:
    int valorA;
};

class ClaseB {
  public:
    ClaseB() : valorB(20) {}
    int LeerValor() const { return valorB; }
  protected:
    int valorB;
};

class ClaseC : public ClaseA, public ClaseB {};

int main() {
  ClaseC CC;

  // cout << CC.LeerValor() << endl;
  // Produce error de compilación por ambigüedad.
  cout << CC.ClaseA::LeerValor() << endl;
 
  return 0;
}

Una solución para resolver la ambigüedad es la que hemos adoptado en el ejemplo. Pero existe otra, también podríamos haber redefinido la función "LeerValor" en la clase derivada de modo que se superpusiese a las funciones de las clases base.
Código:

#include <iostream>
using namespace std;

class ClaseA {
  public:
    ClaseA() : valorA(10) {}
    int LeerValor() const { return valorA; }
  protected:
    int valorA;
};

class ClaseB {
  public:
    ClaseB() : valorB(20) {}
    int LeerValor() const { return valorB; }
  protected:
    int valorB;
};

class ClaseC : public ClaseA, public ClaseB {
  public:
    int LeerValor() const { return ClaseA::LeerValor(); }
};

int main() {
  ClaseC CC;

  cout << CC.LeerValor() << endl;
 
  return 0;
}

Constructores de clases con herencia múltiple


Análogamente a lo que sucedía con la derivación simple, en el caso de derivación múltiple el constructor de la clase derivada deberá llamar a los constructores de las clases base cuando sea necesario. Por ejemplo, añadiremos constructores al ejemplo anterior:
Código:

#include <iostream>
using namespace std;

class ClaseA {
  public:
    ClaseA() : valorA(10) {}
    ClaseA(int va) : valorA(va) {}
    int LeerValor() const { return valorA; }
  protected:
    int valorA;
};

class ClaseB {
  public:
    ClaseB() : valorB(20) {}
    ClaseB(int vb) : valorB(vb) {}
    int LeerValor() const { return valorB; }
  protected:
    int valorB;
};

class ClaseC : public ClaseA, public ClaseB {
  public:
    ClaseC(int va, int vb) : ClaseA(va), ClaseB(vb) {};
    int LeerValorA() const { return ClaseA::LeerValor(); }
    int LeerValorB() const { return ClaseB::LeerValor(); }
};

int main() {
  ClaseC CC(12,14);

  cout << CC.LeerValorA() << ","
        << CC.LeerValorB() << endl;
 
  return 0;
}

Sintaxis:

Citación :
( :
()
[, ()] {}

Herencia virtual

Supongamos que tenemos una estructura de clases como ésta:



La ClaseD heredará dos veces los datos y funciones de la ClaseA, con la consiguiente ambigüedad a la hora de acceder a datos o funciones heredadas de ClaseA.

Para solucionar esto se usan las clases virtuales. Cuando derivemos una clase partiendo de una o varias clases base, podemos hacer que las clases base sean virtuales. Esto no afectará a la clase derivada. Por ejemplo:

Citación :
class ClaseB : virtual public ClaseA {};

Desde el punto de vista de la ClaseB, no hay ninguna diferencia entre ésta declaración y la que hemos usado hasta ahora. La diferencia estará cuando declaramos la ClaseD. Veamos el ejemplo completo:

Citación :
class ClaseB : virtual public ClaseA {};
class ClaseC : virtual public ClaseA {};
class ClaseD : public ClaseB, public ClaseC {};

Ahora, la ClaseD sólo heredará una vez la ClaseA. La estructura quedará así:



Cuando creemos una estructura de este tipo, deberemos tener cuidado con los constructores, el constructor de la ClaseA deberá ser invocado desde el de la ClaseD, ya que ni la ClaseB ni la ClaseC lo harán automáticamente.

Veamos esto con el ejemplo de la clase "Persona". Derivaremos las clases "Empleado" y "Estudiante", y crearemos una nueva clase "Becario" derivada de estas dos últimas. Además haremos que la clase "Persona" sea virtual, de modo que no se dupliquen sus funciones y datos.
Código:

#include <iostream>
#include <cstring>
using namespace std;

class Persona {
  public:
    Persona(char *n) { strcpy(nombre, n); }
    const char *LeeNombre() const { return nombre; }
  protected:
    char nombre[30];
};

class Empleado : virtual public Persona {
  public:
    Empleado(char *n, int s) : Persona(n), salario(s) {}
    int LeeSalario() const { return salario; }
    void ModificaSalario(int s) { salario = s; }
  protected:
    int salario;
};

class Estudiante : virtual public Persona {
  public:
    Estudiante(char *n, float no) : Persona(n), nota(no) {}
    float LeeNota() const { return nota; }
    void ModificaNota(float no) { nota = no; }
  protected:
    float nota;
};

class Becario : public Empleado, public Estudiante {
  public:
    Becario(char *n, int s, float no) :
        Empleado(n, s), Estudiante(n, no), Persona(n) {} (1)
};

int main() {
  Becario Fulanito("Fulano", 1000, 7);

  cout << Fulanito.LeeNombre() << ","
        << Fulanito.LeeSalario() << ","
        << Fulanito.LeeNota() << endl;
 
  return 0;
}

Si observamos el constructor de "Becario" en (1), veremos que es necesario usar el constructor de "Persona", a pesar de que el nombre se pasa como parámetro tanto a "Empleado" como a "Estudiante". Si no se incluye el constructor de "Persona", el compilador genera un error.

Funciones virtuales puras

Una función virtual pura es aquella que no necesita ser definida. En ocasiones esto puede ser útil, como se verá en el siguiente punto.

El modo de declarar una función virtual pura es asignándole el valor cero.

Sintaxis:

Citación :
virtual () = 0;

Clases abstractas

^

Una clase abstracta es aquella que posee al menos una función virtual pura.

No es posible crear objetos de una clase abstracta, estas clases sólo se usan como clases base para la declaración de clases derivadas.

Las funciones virtuales puras serán aquellas que siempre se definirán en las clases derivadas, de modo que no será necesario definirlas en la clase base.

A menudo se mencionan las clases abstractas como tipos de datos abstractos, en inglés: Abstract Data Type, o resumido ADT.

Hay varias reglas a tener en cuenta con las clases abstractas:

* No está permitido crear objetos de una clase abstracta.
* Siempre hay que definir todas las funciones virtuales de una clase abstracta en sus clases derivadas, no hacerlo así implica que la nueva clase derivada será también abstracta.

Para crear un ejemplo de clases abstractas, recurriremos de nuevo a nuestra clase "Persona". Haremos que ésta clase sea abstracta. De hecho, en nuestros programas de ejemplo nunca hemos declarado un objeto "Persona". Veamos un ejemplo:

Código:
#include <iostream>
#include <cstring>
using namespace std;

class Persona {
  public:
    Persona(char *n) { strcpy(nombre, n); }
    virtual void Mostrar() const = 0;
  protected:
    char nombre[30];
};

class Empleado : public Persona {
  public:
    Empleado(char *n, int s) : Persona(n), salario(s) {}
    void Mostrar() const;
    int LeeSalario() const { return salario; }
    void ModificaSalario(int s) { salario = s; }
  protected:
    int salario;
};

void Empleado::Mostrar() const {
    cout << "Empleado: " << nombre
        << ", Salario: " << salario
        << endl;
}

class Estudiante : public Persona {
  public:
    Estudiante(char *n, float no) : Persona(n), nota(no) {}
    void Mostrar() const;
    float LeeNota() const { return nota; }
    void ModificaNota(float no) { nota = no; }
  protected:
    float nota;
};

void Estudiante::Mostrar() const {
    cout << "Estudiante: " << nombre
        << ", Nota: " << nota << endl;
}

int main() {
  Persona *Pepito = new Empleado("Jose", 1000); // (1)
  Persona *Pablito = new Estudiante("Pablo", 7.56);
  char n[30];

  Pepito->Mostrar();
  Pablito->Mostrar();
 
  delete Pepito;
  delete Pablito;
 
  return 0;
}

La salida será así:

Citación :
Empleado: Jose, Salario: 1000
Estudiante: Pablo, Nota: 7.56

En este ejemplo combinamos el uso de funciones virtuales puras con polimorfismo. Fíjate que, aunque hayamos declarado los objetos "Pepito" y "Pablito" de tipo puntero a "Persona" (1), en realidad no creamos objetos de ese tipo, sino de los tipos "Empleado" y "Estudiante"

Uso de derivación múltiple

Una de las aplicaciones de la derivación múltiple es la de crear clases para determinadas capacidades o funcionalidades. Estas clases se incluirán en la derivación de nuevas clases que deban tener dicha capacidad.

Por ejemplo, supongamos que nuestro programa maneja diversos tipos de objetos, de los cuales algunos son visibles y otros audibles, incluso puede haber objetos que tengan las dos propiedades. Podríamos crear clases base para visualizar objetos y para escucharlos, y derivaríamos los objetos visibles de las clases base que sea necesario y además de la clase para la visualización. Análogamente con las clases para objetos audibles.
Volver arriba Ir abajo
Contenido patrocinado





MensajeTema: Re: Objetos y Clases en c++   Hoy a las 5:52 am

Volver arriba Ir abajo
 

Objetos y Clases en c++

Ver el tema anterior Ver el tema siguiente Volver arriba 
Página 1 de 1.

Permisos de este foro:No puedes responder a temas en este foro.
FILECERU :: Desarrollo y Programación :: Detalles :: Utilidades y códigos-