Documente Academic
Documente Profesional
Documente Cultură
Introduccin
Las variables de los tipos fundamentales de datos no son suficientes para modelar adecuadamente
objetos del mundo real. Por ejemplo, no se puede modelar una caja mediante un int, pero si se definen
las variables largo, ancho y alto para representar las dimensiones de una caja, se las puede juntar en una
estructura de datos llamada Caja. A continuacin es posible definir variables de este nuevo tipo de la
misma manera que con variables de tipos bsicos. Se pueden crear, manipular y destruir tantos objetos
de tipo Caja como se quiera [4]. De esta manera puede verse cmo C++ incorpora la nocin de clase
del paradigma de Orientacin a Objetos. La palabra class es la palabra claave para implementar este
concepto.
class Caja {
double largo;
double ancho;
double alto;
};
Las variables que se definen como parte de la clase se llaman datos miembro de la clase.
Se puede hacer una declaracin de una variable de esta clase, digamos cajaGrande, que representa
una instancia de tipo Caja como la siguiente:
Caja cajaGrande;
Una vez que se ha definido la clase Caja, las declaraciones de variables de este tipo son estndares.
Estas variables son instancias de la clase y se las llama objetos.
Clases
La definicin de una clase es la especificacin de un nuevo tipo de dato. Puede contener
elementos que pueden tener variables tanto de los tipos bsicos como de otros tipos definidos por el
usuario. Pueden ser elementos simples o arreglos, punteros, arreglos de punteros, etc. Adems una clase
puede contener funciones que operan sobre los objetos de esa clase accediendo a sus elementos. De esta
manera, una clase combina la definicin de los datos que componen un objeto y los medios para
manipularlos.
Los datos y funciones de una clase son llamados miembros de la clase. Las funciones miembro,
a veces, tambin son llamadas mtodos. A los datos miembro se los suele llamar campos.
Cuando se define una clase, no se define un dato, sino qu significa el nombre de la clase, en qu
consiste un objeto de esa clase y qu operaciones pueden realizarse sobre los objetos de esa clase [4].
class Caja {
double largo;
double ancho;
double alto;
};
El nombre de la clase aparece siguiendo la palabra clave class, y los tres datos miembro se declaran
entre llaves. La definicin de la clase completa debe terminar con punto y coma. Los nombres de todos
los miembros de la clase son locales a la clase, por lo que se puede utilizar los mismos nombres en
cualquier parte del programa sin causar inconvenientes.
Recordemos que cuando se define una clase, que es un tipo de datos, no se declara ningn objeto del tipo
de la clase. Cuando hablamos de acceso a un miembro de la clase, por ejemplo el alto, estamos hablando
acerca del acceso al miembro de datos de un objeto particular, que debe ser definido en algn momento.
Caja caja1;
Caja caja2;
Cada objeto de la clase Caja (caja1 y caja2), tiene sus propios datos miembro [4]. Esto se muestra
en la figura:
caja1 caja2
Los campos miembro no estn inicializados, contienen basura, por lo que se necesita alguna forma de
acceder a ellos.
Una funcin miembro de una clase es una funcin que tiene su definicin o su prototipo dentro de la
definicin de la clase.
Las funciones miembro siempre tienen acceso a todos los campos de la clase. Por ejemplo, si deseamos
proveer a la clase Caja de una funcin que retorne el volmen de la caja desde el cual se la invoca,
podemos escribir:
class Caja {
double largo;
double ancho;
double alto;
public:
double Volumen() {
return largo * ancho * alto;
}
};
En la definicin de la clase, contamos con la seccin de los miembros privados (private), que es el
acceso por defecto y la seccin de los miembros pblicos (public), que son los que aparecen despus
de la etiqueta indicadora.
Si la definicin de una clase miembro se desea realizar fuera de la definicin de la clase, se debe poner
el prototipo de la funcin dentro de la clase. Como la definicin de la funcin aparece afuera de la clase,
debe haber una manera de decirle al compilador que esa funcin pertenece a la clase. Esto se realiza
anteponiendo al nombre de la funcin el nombre de la clase, y separando ambos por el operador de
resolucin de alcance, '::'. Lo ilustramos en el siguiente ejemplo:
class Caja {
double largo;
double ancho;
double alto;
public:
double Volumen();
};
Caja::Volumen() {
return largo * ancho * alto;
}
Esto produce el mismo efecto que el ejemplo anterior, pero no genera exactamente el mismo programa.
En el segundo caso, todos los llamados a la funcin son tratatos de la manera habitual. Sin embargo, si la
definicin de la funcin est dentro de la definicin de la clase, el compilador considera implcitamente
a la misma como una funcin inline [4]. Con una funcin inline el compilador expande el cdigo del
cuerpo de la funcin en el lugar del llamado a la misma. Esto hace que el cdigo sea ms rpido.
#include <iostream>
using std::cout;
using std::endl;
class Caja {
double largo;
double ancho;
double alto;
public:
Caja (double lar, double an, double al) {
largo = lar;
ancho = an;
alto = al;
}
double Volumen();
};
int main() {
Caja unaCaja(0.58, 0.25, 0.55);
cout << "Volmen de la caja: " << unaCaja.Volumen();
}
Observemos que al invocar la funcin Volumen para la instancia de Caja unaCaja se utiliza el
operador punto ('.') llamado operador de seleccin directa de miembros [2].
Un constructor que requiere un nico argumento no necesita ser llamado explcitamente. Por ejemplo:
Caja v = 0.8;
es lo mismo que
Caja c(0.8);
Caja () {};
El mismo puede ser definido por el programador, pero en el caso de que no se provea ningn constructor
en una clase, el compilador generar un constructor por defecto [4]. En cualquiera de los dos casos, es el
constructor que se invoca en una sentencia como la que vimos anteriormente:
Caja caja1;
Listas de inicializacin
Puede utilizarse una tcnica alternativa para inicializar campos en un constructor llamada Listas de
inicializacin, cuyo formato puede verse en el siguiente ejemplo:
Es muy comn, que utilizando este estilo, el cuerpo del constructor quede vaco.
En este caso se inicializa un nuevo objeto llamado otraCaja con las mismas dimensiones que el
objeto unaCaja. En las clases que poseen punteros o arreglos como campos miembro, el constructor de
copia por defecto no funciona como se espera, pues los punteros se copian y los punteros del nuevo
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 6
objeto quedan apuntando a los miembros del primero. En este caso, el programador debe proveer un
constructor de copia.
Cuando se implementa un constructor de copia, el parmetro debe ser una referencia constante:
double getAlto() {
return alto;
}
En general, se definen funciones similares para cada campo que se quiera hacer disponible desde fuera
de la clase sin perjudicar la seguridad de la misma.
Funciones amigas
Existen algunas cirscunstancias donde se desea que algunas funciones que no son miembros de una clase,
tengan acceso a todos los miembros privados de la misma [4]. Tales funciones son llamadas amigas de
la clase. Se las declara anteponiendo la palabra clave friend.
Se debe tener en cuenta que si bien el prototipo de las funciones amigas se declara dentro de la
definicin de la clase, stas no son miembros de la misma, por lo tanto los atributos de acceso no se
aplican sobre ellas.
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 7
El puntero this
Cuando se ejecuta una funcin miembro, sta contiene un puntero oculto llamado this, que apunta al
objeto mediante el cual se hizo la llamada a la funcin. Cuando durante la ejecucin se accede a un
campo miembro de una clase, por ejemplo alto, en realidad la referencia es this->alto. El nombre
del puntero this es agregado por el compilador a los miembros en las funciones. Tambin se puede
hacer mencin explcita a este puntero [4].
No se puede inicializar un dato miembro esttico en la definicin de la clase. Por ello, la sentencia debe
estar fuera de la clase, por ejemplo de la siguiente manera:
int Caja::cantCajas = 0;
Se puede hacer referencia a un campo miembro esttico desde una instancia de la clase o desde la clase
misma:
Los campos estticos de una clase se crean automticamente cuando el programa comienza y se
inicializan con el valor 0, es por ello que existen a pesar de que no se instancie ningn objeto de la clase.
Cuando se declara como esttica una funcin, se la hace independiente de cualquier objeto de la clase.
La ventaja es que existe y puede ser invocada antes de que se instancie cualquier objeto. Estas funciones,
al igual que los campos miembro estticos, pueden llamarse desde un objeto instanciado o a partir del
nombre de la clase.
Destructores
Un destructor es una funcin miembro que destruye un objeto cuando ya no se lo necesita o cuando
queda fuera de alcance. Tiene el mismo nombre que la clase pero precedido con tilde (~). No retorna
ningn valor y tampoco toma ningn parmetro, por lo que slo puede existir un nico destructor por
clase [4].
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 8
Si no se define un destructor para una clase, el compilador siempre genera un destructor por defecto,
pero ste no elimina objetos que han alocado memoria con el operador new. Por eso se debe definir el
destructor con el correspondiente operador delete.
Por ejemplo, si un constructor de una clase ClaseA es:
~ClaseA() {
delete[] mensaje;
}
Sobrecarga de operadores
La sobrecarga de operadores permite que los operadores estndares de C++ puedan programarse para
operar con los tipos de datos o clases definidas por el usuario. No est permitido inventar nuevos
operadores ni cambiar la precedencia de los existentes. Obviamente, se espera que el comportamiento de
un operador sobrecargado sea consistente con el uso normal que se le da al mismo y razonablemente
intuitivo [4].
Ejemplo:
Sobrecargamos el operador < (menor) para la clase Caja, considerando que 'esta' caja es menor que
otra si tiene menor volmen.
Aqu operator es una palabra clave, que junto con el operador (separado por espacio o no) definen el
nombre de la funcin. Notar que la funcin se define const porque no modifica los datos miembro de
la clase. Tambin el argumento debe ser una referencia constante.
El operador < es binario infijo, es decir, toma un operando a la izquierda y un operando a la derecha. En
este caso, podremos escribir
if (caja1 < caja2) cout << "La caja1 es menor que la caja2 ";
caja1.operator<(caja2)
Clases derivadas
Introduccin
C++ proporciona construcciones del lenguaje que soportan directamente la idea del diseo de sistemas
que indica: las clases deben usarse para modelar conceptos en el mundo del programador y de la
aplicacin [1].
Un concepto no existe en forma aislada. Coexiste con otros prximos a l y, gran parte de su fuerza
radica en las relaciones entre ellos. Si se piensa en un auto, rpidamente se lo asocia con las nociones de:
rueda, motor, conductor, peatn, camin, ambulancia, carreteras, gasolina, exceso de velocidad, etc.
Puesto que, usamos clases para representar los conceptos, la cuestin es cmo representar las relaciones
entre ellos. [1]
La mente humana [2] clasifica los conceptos de acuerdo a dos dimensiones: pertenencia y variedad.
Puede decirse que un taxi es un tipo especial de auto (relacin de variedad o, en ingls, una relacin de
tipo is a) y que, una rueda es parte de un auto (relacin de pertenencia o, en ingls, una relacin de tipo
has a). C++ permite implementar ambos tipos de relaciones. La combinacin de ambos tipos de
relaciones es potente: la relacin de pertenencia permite el agrupamiento fsico de estructuras
relacionadas lgicamente y la de variedad o herencia permite que estos grupos de aparicin frecuente se
reutilicen con facilidad en distintas abstracciones.
La nocin de clase derivada y los mecanismos del lenguaje asociados a la misma sirven para expresar
relaciones jerrquicas, es decir, para caracterizar aspectos comunes entre las clases. Por ejemplo, los
conceptos de crculo y tringulo estn relacionados por cuanto ambos son formas, o sea, tienen en
comn el concepto de forma. As pues, debe definirse explcitamente las clases Circulo y Triangulo de
modo que tengan en comn una clase FormaGeometricaPlana. Si se representara un crculo y un
tringulo, en un programa, sin que intervenga la nocin de forma geomtrica plana se perdera algo
esencial. [1]. La herencia implica una relacin de generalizacin/especializacin en la que, una clase
derivada especializa el comportamiento o estructura ms general de sus clases bases [3]. Realmente esta
es la piedra de toque de la herencia: si B no es un tipo de A, entonces B no debera heredar de A. Las
clases bases representan abstracciones generalizadas y, las clases derivadas representan
especializaciones en las que, los datos y funciones miembros de la clase base, sufren aadidos,
modificaciones o incluso ocultaciones.
Una de las ventajas del mecanismo de derivacin de clases es la posibilidad de reutilizar cdigo si tener
que escribirlo nuevamente; las clases derivadas pueden utilizar cdigo de la clase base de la jerarqua,
sin tener que volver a definirlo en cada una de ellas.
Clases derivadas
Considere la construccin de una aplicacin que maneje cajas de distintos tipos. La clase Caja [4],
define una caja en trminos de sus dimensiones ms un conjunto de funciones pblicas que podran
aplicarse a objetos de tipo Caja para resolver problemas asociados a las mismas. La misma tendra este
aspecto:
//Archivo Caja.cpp
#include "caja.h"
#include <iostream>
using std::cout;
using std::endl;
double Caja::volumen(void)
{
return largo*ancho*alto;
}
Caja::~Caja(void)
{
//cout << "Se invoca al destructor de Caja" << endl;
}
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 11
En el mundo real hay muchos tipos de cajas de base rectangular; se las podra diferenciar por la clase de
material que contienen, aqul del que estn hechas y en muchas otras formas. Se puede definir una clase
particular de caja de base rectangular con las caractersticas genricas de todas ellas: alto, ancho, largo y,
aadir algunas caractersticas adicionales al tipo de caja bsica para diferenciarlas del resto.
Probablemente habr nuevas cosas para hacer con este tipo de cajas que no se pueda hacer con otras o,
algunas acciones necesitarn redefinirse.
Para representar todo esto puede definirse un tipo genrico de cajas de base rectangular con las
caractersticas bsicas y especificar otras clases de cajas como especializaciones de ellas. En la figura se
ilustra un ejemplo de estas relaciones que podran definirse entre diferentes clases de cajas de base
rectangular.
La clase CCrate (representa una caja de botellas) deriva de la clase CBox (representa una caja genrica)
y, a la inversa, CBox es la clase base de CCrate. La clase CCrate tendr los miembros propios de la
clase CBox (alto, ancho, largo) adems de los propios (nrobotellas, representando la cantidad de botellas
que puede contener la caja). A menudo se representa grficamente la derivacin mediante un puntero
desde la clase derivada (tal como se ve en la figura) hasta su clase base, para indicar que la clase
derivada se refiere a su base (y no al contrario). A medida que nos movemos hacia abajo en el diagrama,
las cajas se vuelven ms especializadas
Con frecuencia se dice [1] que una clase derivada hereda todas las propiedades de su clase base, por lo
que la relacin suele llamarse herencia. Lo mismo ocurre con las funciones miembros, con algunas
restricciones, entre otras: la clase derivada no hereda el constructor y destructor de la clase base como
tampoco el operador de asignacin sobrecargado en dicha clase ni funciones y/o datos miembros
estticos de la clase base; las clases derivadas tendrn sus propias versiones de constructor, destructor y
operador de asignacin sobrecargado.
Cuando se dice que estas funciones no se heredan no significa que no existen como miembros en un
objeto de la clase derivada; ellas existen an para la parte de la clase base que conforma un objeto de la
clase derivada.
A veces se denomina a la clase base superclase y a la clase derivada subclase. Sin embargo, esta
terminologa induce a error a quien observa que los datos de un objeto de clase derivada son un
superconjunto de los datos de un objeto de la clase base. Una clase derivada es mayor que su clase base
en el sentido que, usualmente contiene ms datos y proporciona ms funciones que esta ltima.
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 12
En una implementacin grfica muy conocida y eficiente de la nocin de clase derivada, un objeto de la
clase derivada, se representa como uno de la clase base aadiendo al final los atributos que pertenecen
especficamente a la clase derivada.
Por ejemplo:
Caja
largo
alto
ancho
CajaBotella
largo
alto
ancho
nrobotellas
Derivar CajadeBotellas de Caja de esta forma, hace que, CajadeBotellas pueda usarse en todos los
lugares de un programa donde sea aceptable Caja. Es decir, una CajadeBotellas es (tambin) una Caja
por lo que, puede usarse un puntero CajadeBotellas* como uno Caja* sin conversin explcita de tipos.
Sin embargo una Caja no es necesariamente una CajadeBotellas por lo que, no puede usarse un puntero
Caja* como uno CajadeBotellas*, la conversin tiene que ser explcita (cast).
Dicho de otro modo, un objeto de una clase derivada puede tratarse como un objeto de su clase base,
cuando se manipula a travs de punteros y referencias. Lo contrario no es cierto. Esta propiedad es muy
utilizada para construir listas de objetos heterogneos (conectados mediante relaciones de herencia).
Slo hay que tener en cuenta que, de este modo slo puede referirse a miembros de la clase base
(funciones y datos). Si mediante el puntero de tipo de la clase base se hace referencia a miembros
(funciones y datos) que figuran slo en la clase derivada, el compilador informar de un error de sintaxis
[5].
Para derivar la clase CajaBotellas de Caja, debemos agregar la directiva #include para el archivo
cabecera Caja.h, debido a que la clase Caja est en el cdigo de la clase derivada. El nombre de la clase
base aparece despus del nombre de la clase derivada CajaBotellas y separada por :, sino se especifica
nada ms, el compilador supone que el estatus de los miembros heredados en la clase derivada es
privado: class CajaBotellas :public Caja
En este caso y en todos los que trabajemos, supondremos que el especificador de acceso para la clase
base es public; todos los miembros heredados y especificados originalmente como public en la clase
base, tiene el mismo nivel de acceso en la clase derivada.
En caso de jerarqua de clases de un nivel, a esta clase base (en este ejemplo Caja) se la llama clase base
directa y, en el caso de tener una jerarqua de clases (como se ver en un ejemplo posterior con la clase
Contenedor) de ms de un nivel, se conoce como clase base indirecta, a aqulla que no aparece en la
lista de derivacin luego de los dos puntos.
Se aade un nuevo miembro en CajaBotellas para representar la cantidad de botellas que puede contener
la caja, la cual es inicializada en el constructor:
~CajaBotellas(void);
private:
int nrobotellas;
};
//archivo CajaBotellas.cpp
#include "CajaBotellas.h"
#include <iostream>
using std::cout;
using std::endl;
CajaBotellas::CajaBotellas(int nro)
{
nrobotellas=nro;
}
double CajaBotellas::volumen(void)
{
return 0.85*Caja::volumen();//ojo no olvidar :: para invocar volumen() de Caja
}
CajaBotellas::~CajaBotellas(void)
{
//cout << "Se invoca al destructor de CajaBotellas" << endl;
}
#include <iostream>
#include "CajaBotellas.h"
using std::cout;
using std::endl;
int main()
{
Caja caja1(4.0, 3.0, 2.0);
CajaBotellas cajab1;
CajaBotellas cajab2(6);
cout<<"Volumen de caja1: "<<caja1.volumen()<<endl
<<"Volumen de cajab1: "<<cajab1.volumen()<<endl
<<"Volumen de cajab2: "<<cajab2.volumen()<<endl;
return 0;
}
Funciones miembro
La solucin ms limpia para el diseo de jerarquas de clases, es aqulla donde la clase derivada usa slo
los miembros pblicos de su clase base, para acceder a los miembros privados de dicha clase base.
Igualmente existe la posibilidad de utilizar miembros protegidos usando la palabra reservada protected.
Un miembro protegido es como un miembro pblico para la clase derivada pero, es como un miembro
privado para todas las dems clases. Se debe acceder a dichos miembros protegidos nicamente a travs
de una referencia de la clase derivada donde se est utilizando.
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 14
Los miembros de una clase derivada [5] pueden hacer referencia a miembros pblicos y protegidos de la
clase base usando directamente los nombres de los mismos; no es necesario utilizar el operador de
resolucin de mbito.
En la clase CajaBotellas obsrvese que se usa :: el operador de resolucin de mbito en la funcin
miembro volumen(), porque la misma ha sido redefinida; esta reutilizacin de nombres es habitual para
aadir ms funcionalidad en las clases derivadas y evitar reescribir cdigo; se invoca la versin de la
clase base, para que lleve a cabo parte de la nueva tarea. Si se omitiesen los :: el programa caera dentro
de una secuencia de llamadas recursivas infinitas. La redefinicin de un mtodo no hace desaparecer al
original, sin embargo, una funcin miembro redefinida oculta todas las funciones miembros heredadas
con el mismo nombre.
//se supone que el volumen efectivo usado de la caja es el 85% del total
double CajaBotellas::volumen(void)
{
return 0.85*Caja::volumen();//ojo no olvidar :: para invocar volumen() de Caja
}
Constructores y destructores
Algunas clases derivadas necesitan constructores. Si la clase base tiene un constructor hay que invocarlo
y si, dicho constructor necesita argumentos, hay que proporcionarlos.
Aunque los constructores de la clase base no se heredan, son usados para crear la parte heredada de la
clase base, de un objeto de la clase derivada y, esta tarea es responsabilidad del constructor de la clase
base.
En el ejemplo de las cajas se invocaba automticamente el constructor por defecto de la clase base (con
los argumentos por defecto).
Para hacer utilizable la clase derivada CajaBotellas se contempla la posibilidad de especificar las
dimensiones de la caja de botellas, adems del nmero de botellas que pueda contener. Dicho
constructor invoca explcitamente al constructor de la clase base para dar valores iniciales a los datos
miembros que hered de la clase base Caja; la invocacin se realiza al final de la signatura del
constructor de la clase derivada CajaBotellas luego de aadir :, puede observarse que esto coincide con
la forma que puede utilizarse para inicializar datos miembros en un constructor, es decir, al respecto la
clase base acta exactamente igual que un miembro de la clase derivada.
~CajaBotellas(void);
private:
int nrobotellas;
};
#include "CajaBotellas.h"
CajaBotellas::CajaBotellas(int nro)
{
cout << "Se invoca al constructor 1 de CajaBotellas" << endl;
nrobotellas=nro;
double CajaBotellas::volumen(void)
{
return 0.85*Caja::volumen();//ojo no olvidar :: para invocar volumen() de Caja
CajaBotellas::~CajaBotellas(void)
{
cout << "Se invoca al destructor de CajaBotellas" << endl;
}
Sino se invoca explcitamente al constructor de la clase base, el compilador dispone que al ejecutarse el
cdigo, se invoque al constructor por defecto de la clase base (no-arg); sino existe este tipo de
constructor, el compilador indica un error.
Un constructor de la clase derivada puede especificar inicializadores slo para sus propios miembros, no
puede inicializar directamente miembros de su clase base directa, eso es responsabilidad de la
invocacin explicita al constructor de la misma.
Los objetos de una clase se construyen de abajo arriba: primero la base, luego los miembros y a
continuacin la clase derivada. Se destruyen en el orden contrario: primero la clase derivada, luego los
miembros y a continuacin la base.
Se aaden mensajes por pantalla a los constructores y destructores para poder observar esta secuencia al
ejecutar la aplicacin de prueba de dichas clases.
#include <iostream>
#include "CajaBotellas.h"
using std::cout;
using std::endl;
int main()
{
Caja caja1(4.0, 3.0, 2.0);
CajaBotellas cajab1;
CajaBotellas cajab2(6);
CajaBotellas cajab3(1.0, 2.0, 3.0);
cout<<"Volumen de caja1: "<<caja1.volumen()<<endl
<<"Volumen de cajab1: "<<cajab1.volumen()<<endl
<<"Volumen de cajab2: "<<cajab2.volumen()<<endl
<<"Volumen de cajab3: "<<cajab3.volumen()<<endl;
return 0;
}
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 16
Constructor de copia
La copia de los objetos de una clase la definen el constructor de copia y las asignaciones.
Recuerde al definir un constructor de copia en cualquier clase que, debe recibir como parmetro una
referencia a un objeto de dicha clase, para evitar un nmero infinito de invocaciones al mismo; lo que
ocurrira si el argumento se pasa por valor y, por tanto, debe construirse una copia del mismo.
En la clase CajaBotellas se incluye un constructor de copia que se encarga en primer lugar de invocar
explcitamente al constructor de copia de la clase base Caja.
#pragma once
class Caja
{
public:
Caja(double l=1.0, double an=1.0, double al=1.0);
Caja(const Caja& c);
double volumen(void);
~Caja(void);
private:
double largo;
double ancho;
double alto;
};
#include <iostream>
#include "caja.h"
using std::cout;
using std::endl;
Caja::Caja(const Caja& c)
{
largo=c.largo;
ancho=c.ancho;
alto=c.alto;
cout<<"Invocado el constructor por copia de Caja"<<endl;
}
double Caja::volumen(void)
{
return largo*ancho*alto;
}
Caja::~Caja(void)
{
cout << "Se invoca al destructor de Caja" << endl;
}
#pragma once
#include "caja.h"
~CajaBotellas(void);
private:
int nrobotellas;
};
#include "CajaBotellas.h"
#include <iostream>
using std::cout;
using std::endl;
CajaBotellas::CajaBotellas(int nro)
{
cout << "Se invoca al constructor 1 de CajaBotellas" << endl;
nrobotellas=nro;
double CajaBotellas::volumen(void)
{
return 0.85*Caja::volumen();//ojo no olvidar :: para invocar volumen() de Caja
CajaBotellas::~CajaBotellas(void)
{
cout << "Se invoca al destructor de CajaBotellas" << endl;
}
#include <iostream>
#include "CajaBotellas.h"
using std::cout;
using std::endl;
int main()
{
Caja caja1(4.0, 3.0, 2.0);
CajaBotellas cajab1;
CajaBotellas cajab2(6);
CajaBotellas cajab3(1.0, 2.0, 3.0);
CajaBotellas cajab4(cajab3); //usa constructor por copia
Jerarqua de clases
La jerarqua de clases conectadas mediante relaciones de herencia forma estructuras de apariencia
arborescente.
Una clase puede heredar datos y funciones miembros de una o ms clases base, aqu slo
consideraremos la herencia simple (heredar de una nica clase base) y no la mltiple.
Tipos de campos
Una forma de determinar el tipo de un objeto perteneciente a una jerarqua de clases mediante herencia
es incorporar una sentencia switch que luego permita invocar la accin apropiada. Esta solucin expone
a una variedad de problemas potenciales: el programador podra olvidarse de incluir una prueba de tipos
cuando es obligatoria hacerla o de evaluar todos los casos posibles; al modificar un programa basado en
este tipo de solucin, agregando nuevos tipos, el programador podra olvidar insertar los nuevos casos en
dicha sentencia. Cada adicin o eliminacin de una clase requiere [5] la modificacin de todos los
switch del programa; rastrear estas instrucciones puede ser un proceso que consuma mucho tiempo y
propenso a errores. Veremos una solucin ms adecuada.
Funciones virtuales
Se aade a la clase Caja una funcin miembro mostrarVolumen( ) que se encarga de invocar a la funcin
volumen( ) y mostrar en pantalla el valor calculado por dicha funcin. Esta funcin, por pertenecer a la
interfaz pblica de esta clase base es heredada por las clases derivadas de la misma. En la funcion main( )
de una clase de prueba, se invoca dicha funcin para mostrar los datos de una Caja y de una
CajaBotellas, de las mismas dimensiones (en caso de CajaBotellas el volumen debera ser 85% menor
que la Caja de mismas dimensiones). Se observa que al ser invocada produce los mismos resultados.
#include <iostream>
#include "CajaBotellas.h"
using std::cout;
using std::endl;
int main()
{
Caja caja1(4.0, 3.0, 2.0);
CajaBotellas cajab1(4.0, 3.0, 2.0);
Caja* cajap=0; //puntero nulo a la clase base Caja
caja1.mostrarVolumen();
cajab1.mostrarVolumen();//sino es virtual se llama volumen de la clase Caja
cajap=&caja1; //puntero a objeto de la clase base
cajap->mostrarVolumen();
cajap=&cajab1; //puntero a objeto de la clase derivada
cajap->mostrarVolumen();
cout<<endl;
return 0;
}
#pragma once
class Caja
{
public:
Caja(double l=1.0, double an=1.0, double al=1.0);
Caja(const Caja& c);
virtual double volumen(void) const;//se usa virtual en la declaracin de la clase
void mostrarVolumen(void) const
~Caja(void);
#include <iostream>
#include "caja.h"
using std::cout;
using std::endl;
Caja::Caja(const Caja& c)
{
largo=c.largo;
ancho=c.ancho;
alto=c.alto;
cout<<"Invocado el constructor por copia de Caja"<<endl;
}
#pragma once
#include "caja.h"
class CajaBotellas :
public Caja
{
public:
CajaBotellas(int nro=1);
CajaBotellas(double l, double an, double al, int nro=1);
CajaBotellas(const CajaBotellas& cb);
virtual double volumen(void) const;
~CajaBotellas(void);
private:
int nrobotellas;
};
#include "CajaBotellas.h"
#include <iostream>
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 21
using std::cout;
using std::endl;
CajaBotellas::CajaBotellas(int nro)
{
cout << "Se invoca al constructor 1 de CajaBotellas" << endl;
nrobotellas=nro;
CajaBotellas::~CajaBotellas(void)
{
cout << "Se invoca al destructor de CajaBotellas" << endl;
}
Para que una funcin se comporte como virtual en una clase derivada, debe tener el mismo nombre, lista
de parmetros y tipo de retorno que la versin de la clase base. Si la funcin en la clase base es const, en
la clase derivada tambin lo deber ser. Si estos requisitos no se cumplen, el mecanismo de funcin
virtual no funcionar y se usar enlace esttico en tiempo de compilacin.
Se puede usar una funcin virtual en una clase aunque no se derive ninguna otra clase de ella. Tampoco
es necesario que una clase derivada proporcione una versin de una funcin definida como virtual en su
clase base, slo debe proporcionarse una versin adecuada si es necesaria sino, se usa la versin de la
clase base.
Las funciones virtuales constituyen un mecanismo extraordinariamente poderoso; cuando se usan de
manera adecuada proporcionan un buen grado de estabilidad a un programa en evolucin. El trmino
polimorfismo es una de las caractersticas ms importantes de la programacin orientada a objetos.
Mediante el polimorfismo, un nombre (tal como la declaracin de una variable) puede denotar objetos
de muchas clases diferentes, relacionadas por una clase base comn; cualquier objeto denotado por este
nombre es, por tanto, capaz de responder a algn conjunto comn de operaciones de distintas formas [3].
Existe polimorfismo cuando interactan la herencia y el enlace dinmico.
Para obtener polimorfismo en C++ debe usarse funciones virtuales, invocadas a travs de punteros o
referencias. Cuando se manipulan los objetos directamente (en lugar de hacerlo de esta forma indirecta)
su tipo exacto es conocido en tiempo de compilacin y no es necesario polimorfismo en tiempo de
ejecucin.
La invocacin a funciones que estn redefinidas en clases derivadas, pero usando el operador de
resolucin de mbito (::) asegura que no se usa el mecanismo de las funciones virtuales, siempre se
invoca a la versin de la clase base.
#include <iostream>
#include "CajaBotellas.h"
using std::cout;
using std::endl;
int main()
{
Caja caja1(4.0, 3.0, 2.0);
CajaBotellas cajab1(4.0, 3.0, 2.0);
Caja* cajap=0; //puntero nulo a la clase base Caja
caja1.mostrarVolumen();
cajab1.mostrarVolumen();//sino es virtual se llama volumen de la clase Caja
cajap=&caja1; //puntero a objeto de la clase base
cajap->mostrarVolumen();
cajap=&cajab1; //puntero a objeto de la clase derivada
cajap->mostrarVolumen();
cout<<endl;
return 0;
}
Ahora el mecanismo de funciones virtuales garantiza que se invoque la versin correcta de volumen( ).
int main()
{
Caja caja1(4.0, 3.0, 2.0);
CajaBotellas cajab1(4.0, 3.0, 2.0);
Caja* cajap=0; //puntero nulo a la clase base Caja
caja1.mostrarVolumen();
cajab1.mostrarVolumen();//sino es virtual se llama volumen de la clase Caja
cajap=&caja1; //puntero a objeto de la clase base
cajap->mostrarVolumen();
cajap=&cajab1; //puntero a objeto de la clase derivada
cajap->mostrarVolumen();
Output(caja1);
Output(cajab1);
cout<<endl;
return 0;
}
};
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 24
#include "Contenedor.h"
#include <iostream>
using std::cout;
using std::endl;
#pragma once
#include "Contenedor.h"
class Caja: public Contenedor
{
public:
Caja(double l=1.0, double an=1.0, double al=1.0);
Caja(const Caja& c);
virtual double volumen(void) const;
~Caja(void);
protected:
double largo;
double ancho;
double alto;
};
#include <iostream>
#include "Caja.h"
using std::cout;
using std::endl;
Caja::Caja(const Caja& c)
{
largo=c.largo;
ancho=c.ancho;
alto=c.alto;
cout<<"Invocado el constructor por copia de Caja"<<endl;
}
#pragma once
#include "contenedor.h"
class Lata :
public Contenedor
{
public:
Lata(double al=4.0, double d=2.0);
virtual double volumen() const;
~Lata(void);
protected:
double diametro;
double alto;
};
#include "Lata.h"
#include <iostream>
using std::cout;
using std::endl;
const double PI = std::atan(1.0)*4;
#pragma once
#include "caja.h"
class CajaBotellas :
public Caja
{
public:
CajaBotellas(int nro=1);
CajaBotellas(double l, double an, double al, int nro=1);
CajaBotellas(const CajaBotellas& cb);
virtual double volumen(void) const;//agrego para usar ref constante en Output
~CajaBotellas(void);
private:
int nrobotellas;
};
#include "CajaBotellas.h"
#include <iostream>
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 26
using std::cout;
using std::endl;
CajaBotellas::CajaBotellas(int nro)
{
cout << "Se invoca al constructor 1 de CajaBotellas" << endl;
nrobotellas=nro;
CajaBotellas::~CajaBotellas(void)
{
cout << "Se invoca al destructor de CajaBotellas" << endl;
}
#include <iostream>
#include "CajaBotellas.h"
#include "Lata.h"
using std::cout;
using std::endl;
int main(void)
{
//puntero a la clase base abstracta Contenedor que apunta a un objeto Caja nuevo
Contenedor* pc1=new Caja(2.0, 3.0, 4.0);
//puntero a la clase base abstracta Contenedor que apunta a un objeto Lata nuevo
Contenedor* pl1=new Lata(6.5, 3.0);
Lata l1(6.5, 3.0); //crea otra lata igual a la anterior
CajaBotellas cb(2.0, 3.0, 4.0);//crea caja de botellas
pc1->mostrarVolumen();
pl1->mostrarVolumen();
cout<<endl;
//limpia el espacio asignado dinmicamente
delete pc1;
delete pl1;
//inicializa pc1 con la direccin de la lata l1
pc1=&l1;
pc1->mostrarVolumen();
Informtica Aplicada Programacin Orientada a Objetos en C++
Pg. 27
//ahora el puntero pc1 apunta a la direccin de CajaBotellas cb
pc1=&cb;
pc1->mostrarVolumen();
return 0;
}
Referencias
[1] Stroustrup, Bjarne, El lenguaje de programacin C++, 3.a edicin. Addison-Wesley, (1998).
[2] Escuela Superior de Ingenieros Industriales de San Sebastin, UNIVERSIDAD DE NAVARRA,
Aprenda C++ como si estuviera en primero. Disponible en:
http://mat21.etsii.upm.es/ayudainf/aprendainf/Cpp/manualcpp.pdf.
[3] Booch, G., Anlisis y Diseo Orientado a Objetos con Aplicaciones, Addison Wesley (1999).
[4] Horton, Ivor, Ivor Hortons Beginning Visual C++ 2008, Wiley Publishing / WROX Programmer to
Programmer, (2008).
[5] H.M. Deitel, P.J. Deitel, Como programar en C/C++ (2 ed), Prentice-Hall, (1995).