class

 

La clase es un nuevo tipo de dato en C++ definido por el usuario que ofrece ventajas de una estructura y la habilidad para permitir el acceso a datos específicos tan sólo a funciones que también son miembros de la clase. Como tales, las clases son una de las mayores contribuciones de C++ a la programación. Las características avanzadas de la clase incluyen la posibilidad de inicializar y proteger funciones y datos sensibles.

Conceptos fundamentales.

Una clase puede tener como miembros tanto datos como funciones.

Una clase puede incluir partes públicas, privadas y protegidas.

 

Especificadores de acceso

El uso más importante de los especificadores de acceso es implementar la ocultación de la información. Significa que a determinados datos del interior de una clase no se puede acceder por funciones externas a la clase.

q       Acceso publico (public): Define miembros públicos, que son aquellos a los que se puede acceder por cualquier función.

q       Acceso privado (private): Solo se puede acceder por funciones miembro de la misma clase o por funciones y clases amigas.

q       Acceso protegido (protected): Con respeto a las funciones externas, es equivalente al acceso privado, pero con respecto las clases derivadas se comporta como público.

Abstracción de datos

Una característica importante de cualquier lenguaje de programación es la capacidad de crear tipos de datos definidos por el usuario. Aunque se pueden crear en C sus propios tipos, utilizando las palabras reservadas typedef y struct, los tipos resultantes no se pueden integrar fácilmente en el resto del programa. Además, en C, sólo se pueden definir los tipos en términos de datos; es decir, las funciones utilizadas para manipular esos tipos no son parte de la definición del tipo.

Una definición de un tipo que incluye datos y funciones y el modo para encapsular los detalles, se conoce como tipo abstracto de dato. En C++ se implementa mediante el uso de tipos de datos definidos por el usuario, llamados clases. clase = datos + funciones .

Una diferencia importante entre C y C++, es que en C++ se pueden declarar funciones dentro de una estructura (no se requiere declarar punteros a funciones). Las estructuras pueden tener también especificadas regiones de acceso (medios en que se puede controlar el acceso a los datos).

La abstracción de datos en C++ se obtiene con los tipos de datos estructura (struct) y clase (class).

 

Concepto de clase

Una clase es un tipo de dato que contiene uno o más elementos dato llamados miembros dato, y cero, una o más funciones que manipulan esos datos (llamadas funciones miembro). Una clase se puede definir con struct, union o class. La sintaxis es la siguiente:

class nombre_clase
{
miembro1;
miembro2;
...
funcion_miembro1();
funcion_miembro2();
};

Una clase es sintácticamente igual a una estructura, con la única diferencia de que en el tipo class todos los miembros son por defecto privados mientras que en el tipo struct son por defecto públicos.

En C se utiliza el término variable estructura para referirse a una variable de tipo estructura. En C++ no se utiliza el término variable de clase, sino instancia de la clase.

El término objeto es muy importante y no es más que una variable, que a su vez no es más que una instancia de una clase. Por consiguiente una clase es:


class cliente
{
char nom[20];
char num;
};

y un objeto de esta clase se declara: cliente cli;

Una definición de una clase consta de dos partes: una declaración y una implementación. La declaración lista los miembros de la clase. La implementación o cuerpo define las funciones de la clase.

Una de las características fundamentales de una clase es ocultar tanta información como sea posible. Por consiguiente, es necesario imponer ciertas restricciones en el modo en que se puede manipular una clase y de cómo se pueden utilizar los datos y el código dentro de una clase.

Una clase puede contener partes públicas y partes privadas. Por defecto, todos los miembros definidos en la clase son privados. Para hacer las partes de una clase públicas (esto es, accesibles desde cualquier parte del programa) deben declararse después de la palabra reservada public. Todas las variables o funciones definidas después de public son accesibles a las restantes funciones del programa. Dado que una característica clave de la POO es la ocultación de datos, debe tenerse presente que aunque se pueden tener variables públicas, desde un punto de vista conceptual se debe tratar de limitar o eliminar su uso. En su lugar, deben hacerse todos los datos privados y controlar el acceso a ellos a través de funciones públicas.

class artiiculo
{
private:float precio;
char nombre[];
public:void indicar();
};

Por defecto u omisión todo lo declarado dentro de una clase es privado y sólo se puede acceder a ello con las funciones miembro declaradas en el interior de la clase o con funciones amigas.

Los miembros que se declaran en la sección protegida de una clase sólo pueden ser accedidos por funciones miembro declaradas dentro de la clase, por funciones amigas o por funciones miembro de clases derivadas.

A los miembros que se declaran en la región pública de una clase se puede acceder a través de cualquier objeto de la clase de igual modo que se accede a los miembros de una estructura en C.

class alfa

{
int x; //miembros dato privados
float y;
char z;
public:double k; //miembro dato público
void fijar(int,float,char); //funciones miembro públicas
void visualizar();
};

void main()
{
alba obj; //declaración de un objeto
obj.fijar(3,2.1,'a'); //invocar a una función miembro
obj.visualizar(); //invocar a una función miembro
obj.x=4; //error: no se puede acceder a datos privados
obj.k=3.2; //válido: k está en la región pública
}

La definición de funciones miembro es muy similar a la definición ordinaria de función. Tienen una cabecera y un cuerpo y pueden tener tipos y argumentos. Sin embargo, tienen dos características especiales:

a)Cuando se define una función miembro se utiliza el operador de resolución de ámbito (::) para identificar la clase a la que pertenece la función.

b) Las funciones miembro (métodos) de las clases pueden acceder a las componentes privadas de la clase.

Opción 1
class ejemplo
{
int x,y;
public:
void f()
{
cout<<"x= "<<x<<" y= "<<y<<endl;
}
};

Opción 2
class ejemplo
{
int x,y;
public:void f();
};
void ejemplo::f()
{
cout<<"x= "<<x<<" y= "<<y<<endl;
}

En la primera opción la función está en línea (inline). Por cada llamada a esta función, el compilador genera (vuelve a copiar) las diferentes instrucciones de la función. En la segunda opción (la más deseable) la función f se llamará con una llamada verdadera de función.

La declaración anterior significa que la función f es miembro de la clase ejemplo. El nombre de la clase a la cual está asociada la función miembro se añade como prefijo al nombre de la función. El operador :: separa el nombre de la clase del nombre de la función. Diferentes clases pueden tener funciones del mismo nombre y la sintaxis indica la clase asociada con la definición de la función.

Los datos miembro

Los valores de los atributos se guardan en los miembros dato o variables de instancia. Los nombres de dichas variables comienzan por letra minúscula.

Vamos a crear una clase denominada Rectangulo, que describa las características comunes a estas figuras planas que son las siguientes:

rectangulo.gif (956 bytes)

class Rectangulo{
               int x;
               int y;
               int ancho;
               int alto;
               //faltan las funciones miembro
}
 

Funciones miembro

En el lenguaje C++ las funciones miembro se declaran, se definen y se llaman. En el lenguaje Java las funciones miembro o métodos solamente se definen y se llaman.

El nombre de las funciones miembro o métodos comieza por letra minúscula y deben sugerir acciones (mover, calcular, etc.). La definición de una función tiene el siguiente formato:

tipo nombreFuncion(tipo parm1, tipo parm2, tipo parm3){
               //...sentencias
}

Entre las llaves de apertura y cierre se coloca la definición de la función. tipo indica el tipo de dato que puede ser predefinido int, double, etc, o definido por el usuario, una clase cualquiera.

Para llamar a un función miembro o método se escribe

               retorno=objeto.nombreFuncion(arg1, arg2, arg3);

Cuando se llama a la función, los argumentos arg1, arg2, arg3 se copian en los parámetros parm1, parm2, parm3 y se ejecutan las sentencias dentro de la función. La función finaliza cuando se llega al final de su bloque de definición o cuando encuentra una sentencia return

Cuando se llama a la función, el valor devuelto mediante la sentencia return se asigna a la variable retorno.

Cuando una función no devuelve nada se dice de tipo void. Para llamar a la función, se escribe

               objeto.nombreFuncion(arg1, arg2, arg3);

Estudiaremos más adelante con más detalle como se definen las funciones.

Una función suele finalizar cuando llega al final del bloque de su definición

void funcion(....){
//sentencias...
}

Una función puede finalizar antes del llegar al final de su definición

void funcion(....){
//sentencias...
               if(condicion) return;
//sentencias..
}

Una función puede devolver un valor (un tipo de dato primitivo o un objeto).

 double funcion(....){
               double suma=0.0;
//sentencias...
               return suma;
}

Cualquier variable declarada dentro de la función tiene una vida temporal, existiendo en memoria, mientras la función esté activa. Se trata de variables locales a la función. Por ejemplo:

void nombreFuncion(int parm){
               //...
               int i=5;
               //...
}

La variable parm, existe desde el comienzo hasta el final de la función. La variable local i, existe desde el punto de su declaración hasta el final del bloque de la función.

Se ha de tener en cuenta que las funciones miembro tienen acceso a los miembros dato, por tanto, es importante en el diseño de una clase decidir qué variables son miembros dato, qué variables son locales a las funciones miembro, y qué valores les pasamos a dichas funciones. Los ejemplos nos ayudarán a entender esta distinción.

Hemos definido los atributos o miembros dato de la clase Rectangulo, ahora le vamos añadir un comportamiento: los objetos de la clase Rectangulo o rectángulos sabrán calcular su área, tendrán capacidad para trasladarse a otro punto del plano, sabrán si contienen en su interior un punto determinado del plano.

La función que calcula el área realizará la siguiente tarea, calculará el producto del ancho por el alto del rectángulo y devolverá el resultado. La función devuelve un entero es por tanto, de tipo int. No es necasario pasarle datos ya que tiene acceso a los miembros dato ancho y alto que guardan la anchura y la altura de un rectángulo concreto.

class Rectangulo{
               int x;
               int y;
               int ancho;
               int alto;
   int calcularArea(){
               return (ancho*alto);
   }
}

A la función que desplaza el rectángulo horizontalmente en dx, y verticalmente en dy,  le pasamos dichos desplazamientos, y a partir de estos datos actualizará los valores que guardan sus miembros dato x e y. La función no devuelve nada es de tipo void.

class Rectangulo{
               int x;
               int y;
               int ancho;
               int alto;
   void desplazar(int dx, int dy){
               x+=dx;
               y+=dy;
   }
}

La función que determina si un punto está o no en el interior del rectángulo, devolverá true si el punto se encuentra en el interior del rectángulo y devolverá false si no se encuentra, es decir, será una función del tipo boolean. La función necesitará conocer las coordenadas de dicho punto. Para que un punto de coordenadas x1 e y1 esté dentro de un rectángulo cuyo origen es x e y, y cuyas dimensiones son ancho y alto, se deberá cumplir a la vez cuatro condiciones

x1>x  y a la vez x1<x+ancho

También se debe cumplir

y1>y  y a la vez y1<y+alto

Como se tienen que cumplir las cuatro condiciones a la vez, se unen mediante el operador lógico AND simbolizado por &&.

class Rectangulo{
               int x;
               int y;
               int ancho;
               int alto;
   boolean estaDentro(int x1, int y1){
               if((x1>x)&&(x1<x+ancho)&&(y1>y)&&(y1<y+ancho)){
                               return true;
               }
               return false;
   }
}

En el lenguaje Java, si la primera condición es falsa no se evalúan las restantes expresiones ya que el resultado es false. Ahora bien, si la primera es verdadera true, se pasa a evaluar la segunda, si ésta el falsa el resultado es false, y así sucesivamente.

 //PROGRAMA DE NUMERO COMPLEJO CON LAS PARTES DE UNA CLASE

#include<iostream.h>

#include<conio.h>

 

class numero_complejo      //defino la clase

{

            private:

            float parte_real;

            float parte_imaginaria;

           

            public:

            numero_complejo();             //constructor

            ~numero_complejo();                      //destructor

           

            void lee_numero(float,float);

            float regresa_real();                         //declaracion de

            float regresa_imaginario();             //funciones miembro

            void imprime_numero();

};

//definición de las funciones miembro

 

numero_complejo::numero_complejo()

{

            parte_real=0;

            parte_imaginaria=0;

}

 

numero_complejo::~numero_complejo()

{

            cout<<"OBJETO DESTRUIDO";

}

 

void numero_complejo::lee_numero(float r,float i)

{

            parte_real=r;

            parte_imaginaria=i;

}

 

float numero_complejo::regresa_real()

{

            return parte_real;

}

 

float numero_complejo::regresa_imaginario()

{

            return parte_imaginaria;

}

 

void numero_complejo::imprime_numero()

{

            cout<<"Numero complejo"<<endl;

            cout<<"\nLa parte real es = "<<parte_real;

            cout<<"\nLa parte imaginaria es ="<<parte_imaginaria;

}

 

void main()    //funcion o programa principal

{

            numero_complejo numerote;

            int a,b,op;

            clrscr();

            do

            {

                        cout<<"ESTE PROGRAMA LEE Y REGRESA UN NUMERO COMPLEJO"<<endl;

                        cout<<"\nIntroduce la parte real";

                        cin>>a;

                        cout<<"\nIntroduce la parte imaginaria";

                        cin>>b;

                        numerote.lee_numero(a,b);

                        numerote.regresa_real();

                        numerote.regresa_imaginario();

                        numerote.imprime_numero();

                        cout<<"Desea volver a realizar este programa?(1=si/2=no)";

                        cin>>op;

            }while(op==1);

            cout<<"ESO ES TODO";

            getch();

 

}

 

Los constructores

Un objeto de una clase se crea llamando a una función especial denominada constructor de la clase. El constructor se llama de forma automática cuando se crea un objeto, para situarlo en memoria e inicializar los miembros dato declarados en la clase. El constructor tiene el mismo nombre que la clase. Lo específico del constructor es que no tiene tipo de retorno.

En síntesis, un constructor es una función miembro especial que sirve para inicializar un objeto de una determinada clase al mismo tiempo que se declara.

Características:

q       El constructor tienen el mismo nombre de la clase donde está definido.

q       Pueden aceptar argumentos y pueden estar sobrecargados.

q       Se ejecuta automáticamente cuando se crea un objeto de tipo clase.

q       Los objetos de almacenamiento dinámico tienen este asignado mediante el operador new, que sirve para reservar memoria para los datos creados.

q       Los constructores los genera C++ si no están definidos explícitamente.

q       No pueden ser heredados.

class Rectangulo{
               int x;
               int y;
               int ancho;
               int alto;
   Rectangulo(int x1, int y1, int w, int h){
               x=x1;
               y=y1;
               ancho=w;
               alto=h;
   }
}

El constructor recibe cuatro números que guardan los parámetros x1, y1, w y h, y con ellos inicializa los miembros dato x, y, ancho y alto.

Una clase puede tener más de un constructor. Por ejemplo, el siguiente constructor crea un rectángulo cuyo origen está en el punto (0, 0).

class Rectangulo{
               int x;
               int y;
               int ancho;
               int alto;
   Rectangulo(int w, int h){
               x=0;
               y=0;
               ancho=w;
               alto=h;
   }
}

Este constructor crea un rectángulo de dimensiones nulas situado en el punto (0, 0),

class Rectangulo{
               int x;
               int y;
               int ancho;
               int alto;
   Rectangulo(){
               x=0;
               y=0;
               ancho=0;
               alto=0;
   }
}

 

Destructor

Es una función miembro de la clase que se utiliza generalmente para liberar la memoria asignada dinámicamente. El destructor tiene el mismo nombre de la clase en la que está definido, precedido por el carácter de tilde ~.

Los destructores son típicamente lo contrario que sus constructores homólogos. Se llaman automáticamente cuando un programa sale del alcance de un objeto de tipo clase o cuando se aplica el operador delete a un apuntador a una clase. A diferencia de un constructor, un destructor no puede aceptar un argumento y no puede estar sobrecargado. Los destructores también pueden ser generados por C++ si no se definen explícitamente.

//PROGRAMA DE FUNCIONES E IDENTIFICACIÓN DE CONSTRUCTOR

//Y DESTRUCTOR

 

#include<iostream.h>

#include<conio.h>

#include<math.h>

#include<process.h>

 

const int cuarto=25;

const int dimes=10;             //DEFINE CONSTANTES ENTERAS

const int nickel=5;

 

class moneda

{

            private:

                        int i;

            public:

                        moneda();                  //CONSTRUCTOR

                        ~moneda();               //DESTRUCTOR

                        void act_valor(double);

                        void lee_pennies(int);

                        float conv_cuartos();

                        float conv_dimes(int);

                        float conv_nickels(int);

};

 

moneda::moneda()

{

            cout<<"comienzo";

}

 

moneda::~moneda()

{          

            cout<<"fin";

}

 

void moneda::lee_pennies(int pen)

{

            i=pen;

            cout<<"centavos convertidos a :";

}

 

float moneda::conv_cuartos()

{

 

            cout<<i/cuarto<<"cuartos";

            return(i%cuarto);

}

 

float moneda::conv_dimes(int d)

{

            cout<<d/dimes<<"dimes";

            return(d%dimes);

}

 

float moneda::conv_nickels(int d)

{

            cout<<d/nickel<<"nickel";

            return(d%nickel);

}

 

main()

{

            moneda cents;

            int c;

            float d,n,p;

            float a;

            clrscr();

            cout<<"Introduzca su efectivo en centavos";

            cin>>c;

            cents.lee_pennies(c);

            a=cents.conv_cuartos();

            n=cents.conv_dimes(a);

            p=cents.conv_nickels(n);

            getch();

 

 

}

           

//PROGRAMA DE SOBRECARGA DE FUNCIONES Y CONSTRUCTORES

 

#include<iostream.h>

#include<conio.h>

 

class alumno

{

            private:                                                                     //LA SOBRECARGA SE

                        int edad;                    //SE DA POR LA CANTIDAD DE            

                        char sexo;                  //ARGUMENTOS IMPLEMENTADOS EN

                                                           //EL CONSTRUCTOR

            public:

                        alumno();

                        alumno(int);

                        alumno(int, char);

                        ~alumno();

 

            void ingresa_datos(int,char);

            void ingresa_datos(char);

            int regresa_edad();

            char regresa_sexo(void);

            void imprime(void);

};

 

alumno::alumno()

{

            edad=23;

            sexo='F';

}

 

alumno::alumno(int e)

{

            edad=e;

            sexo='F';

}

 

alumno::alumno(int e,char s)

{

            edad=e;

            sexo=s;

}

 

alumno::~alumno()

{

            cout<<"\nOBJETIVO DESTRUIDO";

}

 

void alumno::ingresa_datos(char s)

{

            cin>>s;

            edad=0;

            sexo=s;

}

 

 

void alumno::ingresa_datos(int e,char s)

 

{

            edad=0;

            sexo=s;

}

 

int alumno::regresa_edad()

{

            return edad;

}

 

char alumno::regresa_sexo()

{

            return sexo;

}

 

void alumno::imprime()

{

            cout<<"\nLa edad es = "<<edad;

            cout<<"\nEl sexo es = "<<sexo;

}

 

main()

{

 

            alumno a;

            clrscr();

            int e;

            char s;

            cout<<"\POR DEFAULT";

            a.imprime();

            a.ingresa_datos(e,s);

            a.ingresa_datos(s);

            a.regresa_edad();

            a.regresa_sexo();

            a.imprime();

            getch();

}

Herencia

Una de las principales características de las clases es la herencia. Esta propiedad nos permite crear nuevas clases a partir de clases existentes, conservando las propiedades de la clase original y añadiendo otras nuevas.

La nueva clase obtenida se conoce como clase derivada y las clases a partir de las cuales se deriva clases base. Además cada clase derivada puede usarse como base para obtener una nueva clase derivada. Y cada clase derivada puede serlo de una o más clases base. En este caso se conoce como derivación múltiple.

Para las clases derivadas, usar el acceso protected nos permite que los datos sean inaccesibles desde el exterior de las clases, pero a la vez, que sean accesibles desde las clases derivadas.

EJEMPLOS

//PROGRAMA DE UNA CLASE BASE Y UNA DERIVADA

#include<iostream.h>

#include<conio.h>

 

class empleado    //clase base

{

 

private:

            int edad;

 

public:

       empleado();

       ~empleado();

       void Edad(int);

       void ver_edad(void);

};

 

class gerente:public empleado

{

public:

gerente();

~gerente();

}

 

empleado::empleado()

{

clrscr();

cout<<"soy el padre, ya naci";

 

}

 

empleado::~empleado()

{

cout<<"soy el padre ya mori";

}

 

void empleado::Edad (int y)

{

edad=y;

}

 

void empleado::ver_edad (void)

{

 

cout<<"\nLa edad es = "<<edad;

}

 

gerente::gerente()

{

 

cout<<"soy el hijo ya naci";

}

 

gerente::~gerente()

{

 

cout<<"soy el hijo ya mori";

 

}

void main(void)

{

 

gerente alicia;

alicia.Edad(80);

alicia.ver_edad();

getch();

}

//PROGRAMA DE 2 CLASES BASE Y UNA DERIVADA

#include<iostream.h>

#include<conio.h>

 

class uno

{

            protected:

                        int x;

 

            public:

                        uno(int i){x=i;}

 

};

 

class dos

{

            protected:

                        int y;

 

            public:

                        dos(int j){y=j;}

 

};

 

class tres: protected dos, protected uno

{

            int z;

            public:

            tres(int k, int i, int j);

            void ver(void);

};

 

void tres::ver(void)

{

            cout<<"\nEl valor de uno es : "<<x<<"\t";

            cout<<"\nEl valor de dos es : "<<y<<"\t";

            cout<<"\nEl valor de tres es :"<<z;

}

 

void tres::tres (int k, int i, int j): dos(i), uno(j)

{

            z=k;

}

 

void main(void)

{

            clrscr();

            tres x(2,3,4);

            x.ver();

            getch();