Sunteți pe pagina 1din 25

Sobrecarga de operadores

La palabra clave operator declara una funcin especificando el significado


de operator-symbol cuando se aplica a las instancias de una clase. Esto
proporciona al operador ms de un significado, o lo "sobrecarga". El
compilador distingue entre los diferentes significados de un operador
examinando los tipos de sus operandos.
type operator operator-symbol ( parameter-list )
Se puede redefinir la funcin de la mayora de los operadores integrados de
forma global o clase a clase. Los operadores sobrecargados se implementan
como funciones.
El nombre de un operador sobrecargado es operatorx, donde x es el operador
tal como aparece en la tabla siguiente. Por ejemplo, para sobrecargar el
operador de suma, se define una funcin denominada operator+. Del mismo
modo, para sobrecarga el operador de suma y asignacin, +=, se define una
funcin denominada operator+=.

Operadores redefinibles
operador

Nombre

Tipo

Coma

Binary

NOT lgico

Unario

!=

Desigualdad

Binary

Mdulo

Binary

%=

Asignacin y mdulo

Binary

&

AND bit a bit

Binary

&

Direccin de

Unario

&&

AND lgico

Binary

&=

Asignacin AND bit a bit

Binary

()

Llamada a funcin

()

Operador de conversin

Unario

Multiplicacin

Binary

Desreferencia de puntero

Unario

*=

Asignacin y multiplicacin

Binary

Adicin

Binary

Unario ms

Unario

++

Incremento 1

Unario

+=

Asignacin y suma

Binary

Resta

Binary

Negacin unaria

Unario

Decremento 1

Unario

Asignacin y resta

Binary

>

Seleccin de miembro

Binary

>*

Seleccin de puntero a
miembro

Binary

Divisin

Binary

/=

Asignacin y divisin

Binary

<

Menor que

Binary

<<

Desplazamiento a la izquierda

Binary

<<=

Asignacin y desplazamiento a
la izquierda

Binary

<=

Menor o igual que

Binary

Asignacin

Binary

==

Igualdad

Binary

>

Mayor que

Binary

>=

Mayor o igual que

Binary

>>

Desplazamiento a la derecha

Binary

>>=

Asignacin y desplazamiento a
la derecha

Binary

[]

Subndice de matriz

OR exclusivo

Binary

^=

Asignacin y OR exclusivo

Binary

OR inclusivo bit a bit

Binary

|=

Asignacin y OR inclusivo bit


a bit

Binary

||

OR lgico

Binary

Complemento a uno

Unario

delete

Delete

new

New

conversion operators

operadores de conversin

Unario

1 Existen dos versiones de los operadores de incremento y decremento


unarios: preincremento y posincremento.

Reglas generales para la sobrecarga de


operadores
Las reglas siguientes restringen la forma en que se implementan los
operadores sobrecargados. Sin embargo, no se aplican a los
operadores new y delete, que se tratan por separado.

No se pueden definir operadores nuevos, por ejemplo **.

No se puede volver a definir el significado de los


operadores cuando se aplican a los tipos de datos integrados.

Los operadores sobrecargados deben ser una funcin


miembro de clase no esttica o una funcin global. Una funcin global
que necesita acceso a miembros de clase privados o protegidos se debe
declarar como friend de esa clase. Una funcin global debe tomar al
menos un argumento que sea de clase o de tipo enumerado, o que sea
una referencia a una clase o a un tipo enumerado. Por ejemplo:
1)
2)
3)
4)
5)

// rules_for_operator_overloading.cpp
class Point
{
public:
Point operator<( Point & ); // Declare a member
operator
6)
// overload.
7)
// Declare addition operators.
8)
friend Point operator+( Point&, int );
9)
friend Point operator+( int, Point& );
10) };
11)

12) int main()


13) {

14) }

En el ejemplo de cdigo anterior se declara el operador


"menos que" como funcin miembro; sin embargo, los operadores de
suma se declaran como funciones globales con acceso de
confianza. Observe que se puede proporcionar ms de una
implementacin para un operador determinado. En el caso del operador
de suma anterior, las dos implementaciones se proporcionan para
facilitar la propiedad conmutativa. Tambin es probable que puedan
implementarse operadores que agregan Point a Point, int a Point, etc.

Los operadores obedecen a la prioridad, la agrupacin y el


nmero de operandos dictados por su uso tpico con los tipos
integrados. Por consiguiente, no hay ninguna forma de expresar el
concepto "sumar 2 y 3 a un objeto de tipo Point" con la expectativa de
que 2 se sume a la coordenada X y 3 se sume a la coordenada Y.

Los operadores unarios declarados como funcin miembro


no toman ningn argumento; si se declaran como funciones globales,
toman un argumento.

Los operadores binarios declarados como funcin miembro


toman un argumento; si se declaran como funciones globales, toman
dos argumentos.

Si un operador puede utilizarse como operador unario u


operador binario (&, *, + y -), se puede sobrecargar cada uso por
separado.

Los operadores sobrecargados no pueden tener argumentos


predeterminados.

Las clases derivadas heredan todos los operadores


sobrecargados, excepto el de asignacin (operator=).

El primer argumento para los operadores sobrecargados de


funcin miembro siempre es del tipo de clase del objeto para el que se
invoca el operador (la clase en la que se declara el operador o una clase
derivada de ella). No se proporciona ninguna conversin para el primer
argumento.
Observe que el significado de cualquiera de los operadores se puede cambiar
por completo. Esto incluye el significado de los operadores address-of (&), de
asignacin (=) y de llamada a funcin. Adems, las identidades en las que se
puede confiar para los tipos integrados pueden cambiarse mediante la
sobrecarga de operadores. Por ejemplo, las cuatro instrucciones siguientes
suelen ser equivalentes cuando se evalan completamente:
var = var + 1;
var += 1;

var++;
++var;

No se puede confiar en esta identidad para los tipos de clase que sobrecargan
operadores. Adems, algunos requisitos implcitos en el uso de estos
operadores para los tipos bsicos se relajan para los operadores
sobrecargados. Por ejemplo, el operador de suma o asignacin, +=, requiere
que el operando izquierdo sea un valor L cuando se aplica a los tipos bsicos;
este requisito no existe cuando se sobrecarga el operador.

Nota
Por coherencia, a menudo es mejor seguir el modelo de los tipos integrados al
definir operadores sobrecargados. Si la semntica de un operador
sobrecargado difiere mucho de su significado en otros contextos, puede ser
ms confusa que til
Los operadores que se muestran en la tabla siguiente no se pueden
sobrecargar. La tabla incluye los smbolos de preprocesador # y ##.

Operadores no redefinibles
Operator

Name

Seleccin de miembro

.*

Seleccin de puntero a miembro

::

Resolucin de mbito

?:

Condicional

Conversin de preprocesador en una cadena

##

Concatenacin de preprocesadores

Aunque el compilador suele llamar implcitamente a los operadores


sobrecargados cuando se encuentran en el cdigo, se pueden invocar
explcitamente de la misma manera que cualquier funcin miembro o no
miembro:
Point pt;
pt.operator+( 3 );

// Call addition operator to add 3 to pt.

Ejemplo
En el ejemplo siguiente se sobrecarga el operador + para sumar dos nmeros
complejos y se devuelve el resultado.
// operator_overloading.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
struct Complex {
Complex( double r, double i ) : re(r), im(i) {}
Complex operator+( Complex &other );
void Display( ) {
cout << re << ", " << im << endl; }
private:
double re, im;
};
// Operator overloaded using a member function
Complex Complex::operator+( Complex &other ) {
return Complex( re + other.re, im + other.im );
}
int main()
Complex
Complex
Complex

{
a = Complex( 1.2, 3.4 );
b = Complex( 5.6, 7.8 );
c = Complex( 0.0, 0.0 );

c = a + b;
c.Display();
}

LA LEY DE LOS TRES GRANDES


La Regla de Tres es una regla de oro para C++ los tres son funciones miembro
especiales que son creadas automticamente por el compilador si no son
explcitamente creadas por el programador.
Bsicamente diciendo:
Si sus necesidades de la clase cualquiera de un constructor de copia,
Un operador de asignacin,
un destructor,
Si uno de los creados por el compilador no cumple con las necesidades de la
clase, entonces el programador tiene que crearlos explcitamente los tres;
Definido explcitamente, entonces es probable que se necesite todos los tres de
ellos.
Las razones para esto es que todos los tres de ellos se utilizan generalmente
para manejar un recurso, y si la clase gestiona un recurso, suele ser necesario
administrar la copia as como liberadoras.
Si no hay buena semntica para la copia de los recursos de la clase maneja,
considere la posibilidad de prohibir la copia al declarar (no definir) el
constructor de copia y el operador de asignacin como private.
(Nota de que en la prxima nueva versin del estndar de C++ (en la
actualidad, generalmente se refiere a como C++o C++0x 1x) aade mover la
semntica a la de C++, que probablemente va a cambiar la Regla de Tres. Sin
embargo, yo s muy poco acerca de este para escribir un C++1x seccin
acerca de la Regla de Tres.

FUNCIONES DE LOS TRES GRANDES


DESTRUCTORES
Su funcin es destruir los objetos creados por los constructores, liberando los
recursos que fueron asignados a dichos objetos en su proceso de construccin.
CONSTRUCTOR DE COPIA
Nos permite crear un objeto nuevo de la clase idclase a partir de otro ya
existente de la misma clase idclase, por inicializacin, asignando valores a los
miembros datos no inicializados;
OPERADOR DE ASIGNACIN DE COPIA
Es un caso especial del operador de asignacin =sobrecargado, usado para
asignar un objeto a otro, como en:
obja = oba2;
donde obja1 y obja2 son objetos de una misma clase;
cada miembro del lado derecho del objeto es asignado al miembro
correspondiente en el lado izquierdo de manera usual para su tipo;

el operador retorna *this, el cual es una referencia al objeto del lado


izquierdo;

el operador de asignacin de copia difiere del constructor de copia en


que el primero limpia los miembros dato y hace la asignacin mientras
que el otro (el constructor de copia) asigna valores a miembros dato no
inicializados:

La herencia
Se entiende por herencia el proceso por el que un objeto puede tomar
caractersticas de otro objeto.
La herencia se puede usar de dos formas:
1. Cuando una clase escrita no llega a cubrir las necesidades que exige un
determinado problema, se puede retocar el cdigo cambiando las
caractersticas que se requieren, e ignorando el resto.
Esto permite una programacin rpida. Esto es una forma de reutilizacin del
cdigo.
2. Se expresa un determinado problema como una jerarqua de clases, donde
existe una clase base de la que se derivan otras subclases. La interfaz de la
clase base es comn a todas las clases derivadas
CONSTRUCTORES Y DESTRUCTORES EN LA HERENCIA
Los constructores y destructores no son heredados por las clases derivadas.
Sin embargo, una instancia de una clase derivada contendr todos los
miembros de la clase base, y stos deben ser iniciados. En consecuencia, el
constructor dela clase base debe ser llamado por el constructor de la clase
derivada.
CONVERSIONES ENTRE LAS CLASES BASE Y DERIVADAS
Dado que una clase derivada se puede entender como un sper conjunto de la
clase base, que va a contener todos los miembros de la clase base, una
instancia de la clase derivada se puede emplear de forma automtica como si
fuera una instancia de la clase base. El caso contrario no es cierto, no se puede
tratar un objeto de la clase base como si fuera uno bjeto de una clase derivada,
ya que el objeto de la clase base no tiene todos los miembros de la clase
derivada.
TIPOS DE HERENCIA

La relacin de herencia entre clases puede ser: pblica (public), privada


(private) protegida (protected).La relacin que se utiliza con mayor
frecuencia es la pblica. Dependiendo del nmero de clases y de cmo se
relacionen, la herencia puede ser:a) Simple.b) Mltiple.
HERENCIA SIMPLE
Herencia Simple La herencia en C++ es un mecanismo de abstraccin creado
para poder facilitar, y mejorar el diseo de las clases de un programa. Con ella
se pueden crear nuevas clases a partir de clases ya hechas, siempre y cuando
tengan un tipo de relacin especial. En la herencia, las clases derivadas
heredan los datos y la funcin miembro de las clases base, pudiendo las
clases derivadas redefinir estos comportamientos (polimorfismo) y aadir
comportamientos nuevos propios de las clases derivadas.
La forma general de la herencia en C++ es:
class <nombre_clase_derivada>: [<acceso>] <nombre_clase_heredada> {
// Cuerpo de la declaracin de la clase
};
AMBIGEDADES EN LA HERENCIA SIMPLE
Una clase derivada puede tener un miembro con el mismo nombre que un
miembro de la clase base. Cuando ocurre esto, y se referencia este nombre que
est duplicado, el compilador asumir que se desea acceder al miembro de la
clase derivada.
Este hecho se conoce como anulacin, invalidacin o suplantacin del
miembro de la clase base por el miembro de la clase derivada.
Para acceder al miembro de la clase base se deber utilizar el operador de
resolucin de mbito.
HERENCIA MULTIPLE

Es la propiedad con la cual una clase derivada puede tener ms de una clase
base. Es ms adecuada para definir objetos que son compuestos, por
naturaleza, tales como un registro personal, un objeto grfico.
La sintaxis de la herencia mltiple es una extensin de la utilizada para la
herencia simple. La manera deexpresar este tipo de herencia es mediante una
lista de herencia, que consta de las clases de las que se hereda separadas por
comas.
CONSTRUCTORES Y DESTRUCTORES EN HERENCIA MLTIPLE
El uso de funciones constructor y destructor en clases derivadas es ms
complejo que en una clase simple. Al crear clases derivadas con una sola clase
base, se observa que el constructor dela clase base se llama siempre antes que
el constructor de la clase derivada. Adems se dispone de un mecanismo para
pasar los valores de los parmetros necesarios al constructor base desde el
constructor de la clase derivada.
As, la sentencia siguiente pasa el puntero a carcter x y el valor del entero y a
la clase base: derivada (char *x, int y, double z): base(x, y)
Este mtodo se expande para efectuar ms de una clase base.
Las entencia: derivada (char *x,int y, double z): base1(x),base2(y)
AMBIGEDADES EN LA HERENCIA MLTIPLE
En la herencia mltiple aparecen ambigedades, como en el ejemplo de
encima: si la clase Msico heredaba de Persona y Trabajador, y la clase
Trabajador heredaba de Persona.
Existiran las siguientes reglas:
Msico Estudiante: Persona, Msico, Trabajador
Msico: Persona, Trabajador
Trabajador: Persona
Si un compilador est mirando la clase Msico Estudiante necesita saber si
debe juntar las caractersticas iguales o si deben estar separadas. Por ejemplo,
tendra sentido unir las caractersticas "Edad" de Persona para Msico

Estudiante. La edad de una persona no cambia si le consideras una Persona, un


Trabajador o un Msico. Sin embargo, tendra sentido separar la caracterstica
"Nombre" de Persona y Msico si los msicos usan un nombre artstico
diferente de su nombre real. Las opciones de juntar y separar son vlidas
segn el contexto, y slo el programador sabe qu opcin es correcta para la
clase que est diseando.
DEBATE EN LA HERENCIA MULTIPLE
Hay debate sobre si la herencia mltiple puede ser implementada de forma
simple y sin ambigedad. Con frecuencia es criticada por su aumentada
complejidad y su ambigedad, as como los problemas de versiones y
mantenimiento que puede causar (a menudo resumido como el problema del
diamante).
Los detractores tambin sealan que hay problemas de implementacin de la
herencia mltiple como no ser capaces de explicitar herencia de mltiple
clases y el orden de las semnticas de clase que cambian con la herencia. Hay
lenguajes que solucionan todos los problemas tcnicos de la herencia mltiple,
pero el debate principal sigue sobre si implementar y usar herencia mltiple es
ms fcil que usar herencia simple y patrones de diseo de software.

POLIMORFISMO
Habilidad de un mismo objeto para tomar diferentes formas;
En too es la propiedad que tienen objetos pertenecientes a diferentes clases de
poder responder a un mismo mensaje de diferentes formas;

Para que la ligadura dinmica tenga efecto en una funcin particular, C++
necesita que se use la palabra reservada virtual cuando se declara la funcin en
la clase base. La ligadura en tiempo de ejecucin funciona uncamente con las
funciones virtual es, y slo cuando se est usando una direccin de la clase
base donde exista la funcin virtual, aunque puede ser definida tambin en una
clase base anterior.

Para crear una funcin miembro como virtual, simplemente hay que preceder
a la declaracin de la funcin con la palabra reservada virtual. Slo la
declaracin necesita la palabra reservadavirtual, y no la definicin. Si una
funcin es declarada como virtual, en la clase base, ser entonces virtual en
todas las clases derivadas. La redefinicin de una funcin virtual en una clase
derivada se conoce como overriding.
Hay que hacer notar que slo es necesario declarar la funcin como virtual en
la clase base. Todas las funciones de las clases derivadas que encajen con la
declaracin que est en la clase base sern llamadas usando el mecanismo
virtual. Se puede usar la palabra reservada virtual en las declaraciones de las
clases derivadas (no hace ningn mal), pero es redundante y puede causar
confusin.
Para conseguir el comportamiento deseado de Instrument2.cpp, simplemente
hay que aadir la palabra reservada virtual en la clase base antes de play().
//: C15:Instrument3.cpp
// Late binding with the virtual keyword
#include <iostream>
using namespace std;
enum note { middleC, Csharp, Cflat }; // Etc.
class Instrument {
public:
virtual void play(note) const {
cout << "Instrument::play" << endl;
}
};
// Wind objects are Instruments
// because they have the same interface:
class Wind : public Instrument {
public:
// Override interface function:
void play(note) const {
cout << "Wind::play" << endl;
}
};
void tune(Instrument& i) {
// ...
i.play(middleC);
}

int main() {
Wind flute;
tune(flute); // Upcasting
} ///:~

Este archivo es idntico a Instrument2.cpp excepto por la adicin de la palabra


reservada virtualy, sin embargo, el comportamiento es significativamente
diferente: Ahora la salida es Wind::play.
Extensibilidad
Con play() definido como virtual en la clase base, se pueden aadir tantos
nuevos tipos como se quiera sin cambiar la funcin play(). En un programa
orientado a objetos bien diseado, la mayora de las funciones seguirn el
modelo de play() y se comunicarn nicamente a travs de la interfaz de la
clase base. Las funciones que usen la interfaz de la clase base no necesitarn
ser cambiadas para soportar a las nuevas clases.
Aqu est el ejemplo de los instrumentos con ms funciones virtuales y un
mayor nmero de nuevas clases, las cuales trabajan de manera correcta con la
antigua (sin modificaciones) funcinplay():
//: C15:Instrument4.cpp
// Extensibility in OOP
#include <iostream>
using namespace std;
enum note { middleC, Csharp, Cflat }; // Etc.
class Instrument {
public:
virtual void play(note) const {
cout << "Instrument::play" << endl;
}
virtual char* what() const {
return "Instrument";
}
// Assume this will modify the object:
virtual void adjust(int) {}
};
class Wind : public Instrument {
public:
void play(note) const {

cout << "Wind::play" << endl;


}
char* what() const { return "Wind"; }
void adjust(int) {}
};
class Percussion : public Instrument {
public:
void play(note) const {
cout << "Percussion::play" << endl;
}
char* what() const { return "Percussion"; }
void adjust(int) {}
};
class Stringed : public Instrument {
public:
void play(note) const {
cout << "Stringed::play" << endl;
}
char* what() const { return "Stringed"; }
void adjust(int) {}
};
class Brass : public Wind {
public:
void play(note) const {
cout << "Brass::play" << endl;
}
char* what() const { return "Brass"; }
};
class Woodwind : public Wind {
public:
void play(note) const {
cout << "Woodwind::play" << endl;
}
char* what() const { return "Woodwind"; }
};
// Identical function from before:
void tune(Instrument& i) {
// ...
i.play(middleC);
}
// New function:
void f(Instrument& i) { i.adjust(1); }
// Upcasting during array initialization:
Instrument* A[] = {
new Wind,
new Percussion,
new Stringed,

new Brass,

};

int main() {
Wind flute;
Percussion drum;
Stringed violin;
Brass flugelhorn;
Woodwind recorder;
tune(flute);
tune(drum);
tune(violin);
tune(flugelhorn);
tune(recorder);
f(flugelhorn);
} ///:~

Se puede ver que se ha aadido otro nivel de herencia debajo de Wind, pero el
mecanismovirtual funciona correctamente sin importar cuantos niveles haya.
La funcin adjust() no est redefinida (override) por Brass y Woodwind.
Cuando esto ocurre, se usa la definicin ms "cercana" en la jerarqua de
herencia - el compilador garantiza que exista alguna definicin para una
funcin virtual, por lo que nunca acabar en una llamada que no est enlazada
con el cuerpo de una funcin (lo cual sera desatroso).
El array A[] contiene punteros a la clase base Instrument, lo que implica que
durante el proceso de inicializacin del array habr upcasting. Este array y la
funcin f() sern usados en posteriores discusiones.
En la llamada a tune(), el upcasting se realiza en cada tipo de objeto, haciendo
que se obtenga siempre el comportamiento deseado. Se puede describir como
"enviar un mensaje a un objeto y dejar al objeto que se preocupe sobre qu
hacer con l". La funcin virtual es la lente a usar cuando se est analizando
un proyecto: Dnde deben estar las clases base y cmo se deseaextender el
programa? Sin embargo, incluso si no se descubre la interfaz apropiada para la
clase base y las funciones virtuales durante la creacin del programa, a
menudo se descubrirn ms tarde, incluso mucho ms tarde cuando se desee
ampliar o se vaya a hacer funciones de mantenimiento en el programa. Esto no
implica un error de anlisis o de diseo; simplemente significa que no se
conoca o no se poda conocer toda la informacin al principio. Debido a la
fuerte modularizacin de C++, no es mucho problema que esto suceda porque

los cambios que se hagan en una parte del sistema no tienden a propagarse a
otras partes como sucede en C.

MANEJO DE EXCEPCIONES
Las excepciones son en realidad errores durante la ejecucin. Si uno de esos
errores se produce y no implementamos el manejo de excepciones, el
programa sencillamente terminar abruptamente. Es muy probable que si hay
ficheros abiertos no se guarde el contenido de los buffers, ni se cierren,
adems ciertos objetos no sern destruidos, y se producirn fugas de memoria.
En programas pequeos podemos prever las situaciones en que se pueden
producir excepciones y evitarlos. Las excepciones ms habituales son las de
peticiones de memoria fallidas.
Veamos este ejemplo, en el que intentamos crear un array de cien millones de
enteros:
#include <iostream>
using namespace std;
int main() {
int *x = NULL;
int y = 100000000;
x = new int[y];
x[10] = 0;
cout << "Puntero: " << (void *) x << endl;
delete[] x;
return 0;
}

El sistema operativo se quejar, y el programa terminar en el momento que


intentamos asignar un valor a un elemento del array.
Podemos intentar evitar este error, comprobando el valor del puntero despus
del new:

#include <iostream>
using namespace std;
int main() {
int *x = 0;
int y = 100000000;
x = new int[y];
if(x) {
x[10] = 0;
cout << "Puntero: " << (void *) x << endl;
delete[] x;
} else {
cout << "Memoria insuficiente." << endl;
}
return 0;
}

Pero esto tampoco funcionar, ya que es al procesar la sentencia que contiene


el operador new cuando se produce la excepcin. Slo nos queda evitar
peticiones de memoria que puedan fallar, pero eso no es previsible.
Sin embargo, C++ proporciona un mecanismo ms potente para detectar
errores de ejecucin: las excepciones. Para ello disponemos de tres palabras
reservadas extra: try, catch y throw, veamos un ejemplo:
#include <iostream>
using namespace std;
int main() {
int *x;
int y = 100000000;
try {
x = new int[y];
x[0] = 10;
cout << "Puntero: " << (void *) x << endl;
delete[] x;
}
catch(std::bad_alloc&) {
cout << "Memoria insuficiente" << endl;
}
return 0;
}

La manipulacin de excepciones consiste en transferir la ejecucin del


programa desde el punto donde se produce la excepcin a un manipulador que
coincida con el motivo de la excepcin.
Como vemos en este ejemplo, un manipulador consiste en un bloque try,
donde se incluye el cdigo que puede producir la excepcin.
A continuacin encontraremos uno o varios manipuladores asociados al
bloque try, cada uno de esos manipuladores empiezan con la palabra catch, y
entre parntesis una referencia o un objeto.
En nuestro ejemplo se trata de una referencia a un objeto bad_alloc, que es el
asociado a excepciones consecuencia de aplicar el operador new.
Tambin debe existir una expresin throw, dentro del bloque try. En nuestro
caso es implcita, ya que se trata de una excepcin estndar, pero podra haber
un throw explcito, por ejemplo:
#include <iostream>
using namespace std;
int main() {
try {
throw 'x'; // valor de tipo char
}
catch(char c) {
cout << "El valor de c es: " << c << endl;
}
catch(int n) {
cout << "El valor de n es: " << n << endl;
}
return 0;
}

El throw se comporta como un return. Lo que sucede es lo siguiente: el


valor devuelto por el throw se asigna al objeto del catch adecuado. En este
ejemplo, al tratarse de un carcter, se asigna a la variable 'c', en el catch que
contiene un parmetro de tipo char.

En el caso del operador new, si se produce una excepcin, se hace un throw de


un objeto de la clase std::bad_alloc, y como no estamos interesados en ese
objeto, slo usamos el tipo, sin nombre.
El manipulador puede ser invocado por un throw que se encuentre dentro del
bloque try asociado, o en una de las funciones llamadas desde l.
Cuando se produce una excepcin se busca un manipulador apropiado en el
rango del try actual. Si no se encuentra se retrocede al anterior, de modo
recursivo, hasta encontrarlo. Cuando se encuentra se destruyen todos los
objetos locales en el nivel donde se ha localizado el manipulador, y en todos
los niveles por los que hemos pasado.
#include <iostream>
using namespace std;
int main() {
try {
try {
try {
throw 'x'; // valor de tipo char
}
catch(int i) {}
catch(float k) {}
}
catch(unsigned int x) {}
}
catch(char c) {
cout << "El valor de c es: " << c << endl;
}
return 0;
}

En este ejemplo podemos comprobar que a pesar de haber hecho el throw en


el tercer nivel del try, el catch que lo procesa es el del primer nivel.
Los tipos de la expresin del throw y el especificado en el catch deben
coincidir, o bien, el tipo del catch debe ser una clase base de la expresin del
throw. La concordancia de tipos es muy estricta, por ejemplo, no se considera
como el mismo tipo int que unsigned int.

Si no se encontrase ningn catch adecuado, se abandona el programa, del


mismo modo que si se produce una excepcin y no hemos hecho ningn tipo
de manipulacin de excepciones. Los objetos locales no se destruyen, etc.
Para evitar eso existe un catch general, que captura cualquier throw para el
que no exista un catch concreto:

#include <iostream>
using namespace std;
int main() {
try {
throw 'x'; //
}
catch(int c) {
cout << "El valor de c es: " << c << endl;
}
catch(...) {
cout << "Excepcin imprevista" << endl;
}
return 0;
}

Templetes en C++
Los templetes son muy importantes en el desarrollo de software con C++. Es
sin duda una de las caractersticas ms importantes (si no es la ms
importante) de este lenguaje. Nos permite escribir cdigo genrico que puede
ser usado con varios tipos de datos. Sin templetes, se tendran que reescribir
muchas funciones y clases. Tomemos el siguiente ejemplo de una funcin que
retorna el mximo de dos valores.
int max(int x, int y)
{
return (x < y) ? y : x;

}
float max(float x, float y)
{
return (x < y) ? y : x;
}

Como podemos ver, sin templetes dos funciones que tienen el mismo cuerpo
se tienen que reescribir para que funcione con varios tipos. Veamos la misma
funcin usando templetes.
template <typename T>
T max(T x, T y)
{
return (x < y) ? y : x;
}

Esta funcin funciona con cualquier tipo que se pueda comparar con el
operador <. Funciona hasta con tu propia clase si ella tiene el
operador< implementado. Tambin se puede tener templetes para clases.
Clases como std::vector y std::list son templetes y nos permiten usarlas para
cualquier tipo que queramos. Veamos un ejemplo de una clase tmplate.
template <typename T>
class Puntero
{
T* _ptr;
public:
explicit Puntero(T* ptr = 0) : _ptr(ptr) {}
~Puntero() { delete _ptr; }
// implementamos estos operadores para que la clase actue
como puntero
T& operator*() { return *_ptr; }
T* operator->() { return _ptr; }
};
int main()
{
Puntero<int> puntero_int(new int);
*puntero_int = 10;
Puntero<bool> puntero_bool(new bool);
*puntero_bool = true;
}

Ah podemos ver una clase (incompleta) que trata de personificar un puntero.


Cuando se destruye una instancia de esta clase, tambin destruye el puntero
que contena. Ah pueden ver lo fcil que es hacer una clase genrica y los
beneficios que esto trae. Sin templetes, hubiera tenido que copiar la misma
clase dos veces, cambiando int por bool solamente. Ah se va notando que
el reus del cdigo puede ser significativo al usar templetes. Imagnense re
implementar std::vector por cada tipo que se quiera usar

S-ar putea să vă placă și