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 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:

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();
}
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();