SlideShare ist ein Scribd-Unternehmen logo
1 von 12
Downloaden Sie, um offline zu lesen
Programaci´n Modular. ETSIT. 1o C.
                                                                                   o
                                                                       Apuntes del profesor Juan Falgueras.
                                                                                   Curso 2001/02
                                                                            versi´n: 28 de abril de 2003
                                                                                 o

5
Programaci´n orientada a objetos
          o

Contenido
5. Programaci´n orientada a objetos
                o                                                                                                                                               1
   5.1. Introducci´n a C++ . . . . . . . .
                  o                            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    1
   5.2. Diferencias entre C y C++ . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    1
   5.3. Clases . . . . . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    5
   5.4. Definici´n de clases . . . . . . . .
               o                               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    5
   5.5. M´todos . . . . . . . . . . . . . .
          e                                    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    6
   5.6. Constructores y Destructores . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    8
   5.7. Sobrecarga . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
   5.8. Entrada y salida en C++ . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   11


5.     Programaci´n orientada a objetos
                 o


5.1.     Introducci´n a C++
                   o
     El lenguaje C++ fue desarrollado por Bjarne Stroustrup1 de AT&T Bell Laboratories durante
los 80. El autor expandi´ enormemente el lenguaje C para dar soporte a principios de dise˜o m´s
                        o                                                                n   a
modernos. La diferencia m´s importante entre C y C++ est´ en el soporte para clases, pero son
                           a                               a
fundamentales tambi´n:
                     e
 1.    Sobrecarga de operadores, que hace posible dar significados a˜adidos a los operadores tradi-
                                                                   n
       cionales.
 2.    Plantillas (templates), que permiten escribir pre-c´digo muy reutilizable sin concretar a´n
                                                          o                                     u
       elementos variables.
 3. Gesti´n de excepciones, uniformando dando soporte a la detecci´n y respuesta a los errores
         o                                                        o
    de ejecuci´n.
              o

Sin embargo uno de los objetivos que se propuso el autor fue el de mantener a´n compatibilidad con
                                                                             u
el lenguaje germinal C en lo posible. As´ todas las posibilidades del est´ndard C est´n igualmente
                                        ı                                a           a
presentes en C++. Esto, sin embargo no significa que todos los programas escritos en C compilen
en C++; existen restricciones en C++ respecto a C sobre todo en aras de una mayor seguridad.

5.2.     Diferencias entre C y C++
    Adem´s de las grandes novedades relativas a la programaci´n orientada a objetos (clases,
          a                                                        o
sobrecarga, derivaci´n, funciones virtuales, plantillas y gesti´n de excepciones), C++ a˜ade a C
                    o                                          o                        n
peque˜as diferencias que conviene conocer.
      n
  1 http://www.research.att.com/~bs/homepage.html
5.2   Diferencias entre C y C++                                                                               2


Comentarios C++ tiene comentarios de ´mbito limitado a una s´la l´
                                           a                      o ınea, frente a C que no acaba
el comentario al terminar la l´
                              ınea. Como ya habremos visto, los comentarios de C++ empiezan con
// y terminan con el final de la l´  ınea. Esto no quiere decir, por otra parte, que no se puedan
utilizar tambi´n la t´cnica de comentarios de C.
              e      e

Etiquetas frente a nombres de tipo Los identificadores de estructuras, uniones y enumerados
bastan para definir en C++ el propio tipo sin necesidad de crear un tipo con typedef. As´ en vez
                                                                                       ı
de
      typedef struct {int numerador, denominador;} Fracc;
basta (en C++) escribir:
      struct Fracc {int numerador, denominador;};
pudi´ndose desde entonces definir estructuras de tipo Fracc;.
    e

Funciones sin argumentos            No hay necesidad de usar la palabra void cuando se definen fun-
ciones sin argumentos:
      int leeInt(void); // en C
      int leeInt();     // es suficiente en C++

Argumentos con valor por defecto C++ permite que un argumento tome un valor por defecto
cuando no se especifique un valor real en la llamada. Por ejemplo:

      void wrLn(int n = 1) { // tiene un par´metro que toma por defecto el valor 1
                                            a
        while (n-- > 0) putchar(’n’);
      }


puede ser llamado con o sin par´metros
                               a

      wrLn(3);     // escribir´a tres saltos de l´nea
                              ı                  ı
      wrLn();      // escribir´a uno
                              ı


Si se define la funci´n int a(int a=1, int b=2, int c), ¿qu´ significa a(4,5)? Podr´ significar
                    o                                         e                       ıa
varias cosas con lo que los argumentos con valores por defecto siempre deben estar juntos al final
de la lista de argumentos formales. int a(int c, int a=1, int b=2) hace que a(1), ´ a(1,2)
                                                                                         o
´ a(1,2,3) sean todos no-ambiguos.2
o

Funciones inline El lenguaje C explot´ enormemente el uso del preprocesado l´xico. El prepro-
                                           o                                          e
cesador act´a antes que el compilador substituyendo sint´cticamente los elementos #define’dos
            u                                                a
por su equivalente, sin que el preprocesador interprete el significado sino s´lo los tokens l´xicos que
                                                                            o               e
intervienen. Esta idea proven´ de los antiguos lenguajes ensambladores de los que C era cercano
                               ıa
heredero. La #define’ci´n de s´
                         o        ımbolos es s´lo el mecanismo m´s simple; cpp, el preprocesador,
                                              o                     a
previo a C, permite la expansi´n de macros. Una macro es un s´
                                o                                  ımbolo con argumentos. Entonces
se sustituye el s´
                 ımbolo por la expresi´n y los argumentos se intercalan en la expresi´n all´ d´nde
                                       o                                                o      ı o
se indique.
     La potencia sint´ctica del preprocesado de macros de C tiene sin embargo algunos puntos
                      a
oscuros. Imagin´monos un macro
                 e
      #define cuadrado(x) (x*x)
   2 En otros lenguajes es posible poner nombres a los argumentos en la propia llamada, a(1, c=>3) permitir´ que
                                                                                                           ıa
b tomase libremente su valor por defecto. Esto ayuda a manejar funciones con muchos argumentos superfluos.
5.2   Diferencias entre C y C++                                                                                 3


que significa que all´ en el fuente en C d´nde aparezca cuadrado(algo) lo debe substituir por
                    ı                       o
cuadrado(algo*algo). Insistimos, el preprocesador no entra en interpretar lo que signifique nin-
guna de las expresiones anteriores, eso se lo deja al compilador que recoje el resultado del prepro-
cesado. Ve´mosla en acci´n en diversas ocasiones como:
          a              o

      void f(double d, int i)
      {
          r = cuadrado(d);             // bien
          r = cuadrado(i++);           // mal: significa (i++*i++)
          r = cuadrado(d+1);           // mal: significa (d+1*d+1); que es, (d+d+1)
          // ...
      }

El problema de “d+1” se resuelve mediante el a˜adido de par´ntesis en el macro:
                                              n            e
      #define cuadrado(x) ((x)*(x))                   /* mejor */
pero el de “i++” no tiene soluci´n en C. C++ a˜ade un tipo de “funciones” que se expanden
                                  o               n
(sint´cticamente) en el momento de ser utilizadas. As´ no es un preprocesador, el preprocesador de
     a                                               ı
C, que no tiene en cuenta la sem´ntica de las operaciones C, el que se encarga de expandir estas
                                  a
construcciones sint´cticas sino el propio compilador en C++ el que expande de manera correcta
                   a
estos macros, teniendo sobre todo en cuenta la manipulaci´n correcta de los argumentos.
                                                           o
      inline int icuadrado(int x) {return x * x;}
El hecho de que sea el propio compilador el que expanda el cuerpo de la funci´n obliga, sin embargo,
                                                                             o
a que el tipo del argumento sea conocido, algo que C++ no puede resolver excepto con el uso de
plantillas, que son, sin embargo, inadecuadas para este prop´sito.
                                                             o

Paso de par´metros por referencia En C todos los par´metros siempre son pasados por
                a                                                a
valor, esto es, copiando el par´metro que se ponga en la llamada, par´metro actual, a la variable
                               a                                      a
receptora en el par´metro formal ya como variable local del procedimiento. Esto nos obliga a
                     a
utilizar a poner las direcciones de las variables (& en los par´metros de la llamada y a apuntar
                                                               a
mediante esas direcciones (*) dentro del procedimiento para poder modificar los valores de las
variables pasadas.

      void intercambia(int *a, int *b) {
        int temp= *a;
        *a = *b;
        *b = temp;
      }

y es llamado mediante intercambia(&i, &j);. O la tan famosa scanf que require referencias a
los elementos a leer. Por ejemplo:

      for (i=0; i<N; i++)
        scanf("%d", &a[i]);

     Esta t´cnica es inc´moda e insegura3 . C++ detalla y permite un mayor control del paso de
           e            o
par´metros a˜adiendo la posibilidad del paso de par´metros por referencia. En este caso, el par´me-
   a         n                                     a                                           a
tro actual no necesita llevar el indicador de que se va modificar (&) sino que es en el par´metro
                                                                                             a
   3 Aunque esta t´cnica es inc´moda, tambi´n hay que decir que es m´s expl´
                   e           o              e                         a     ıcita en cuanto a la peligrosidad de
uso de este tipo de modificaciones no locales. Es m´s expl´
                                                     a      ıcita porque manifiesta el cambio mediante el signo *
en el procedimiento. Sin embargo respecto a los arrays el tema es m´s oscuro y el cambio se produce siempre ya
                                                                      a
que el propio nombre del array es la direcci´n del bloque de datos del mismo. Recordemos que en C los par´ntesis
                                            o                                                               e
cuadrados [] act´ an como operadores de indirecci´n siempre: x[i] equivale *(x+i)
                 u                                 o
5.2   Diferencias entre C y C++                                                                  4


formal de la declaraci´n del procedimiento donde se indica que se va a recibir al par´metro por
                      o                                                               a
referencia. Esto es mucho m´s c´mo en muchos casos y es preferido por la mayor´ de los pro-
                             a o                                                   ıa
gramadores. Es un mecanismo a˜adido. El compilador se encarga de los aspectos ‘sucios’ de la
                                 n
modificaci´n del par´metro actual. No hay que usar & cada vez que se llame en el par´metro
           o         a                                                                  a
formal ni * en la funci´n cada vez que se quiera acceder al valor del par´metro. En el caso del
                        o                                                a
procedimiento de intercambio anterior escribiremos:

      void intercambia(int& a, int& b) {
        int temp= a;
        a = b;
        b = temp;
      }


y es llamado mediante intercambia(i, j);. N´tese que aparece una nueva notaci´n: se declara
                                                o                              o
un par´metro que es una referencia a un tipo de elemento. C carece de las posibilidades de la
        a
referencia y por ello usa los punteros siempre.

Operadores new y delete En vez de las funciones malloc, calloc, realloc y free, aunque
C++ las puede utilizar, C++ introduce un operador (y por lo tanto una sintaxis diferente) para
adquirir nuevos bloques de memoria y/o crear nuevos objetos en ella. Por ejemplo:
      int *pint,    *pin2, *aint;
      pint = new    int;      // adquiere memoria adecuada para un entero
      pin2 = new    int(33); // .. e inicializa su contenido a 33
      aint = new    int[10]; // ´dem para un array de 10 enteros
                                 ı
Como es de esperar, new devolver´ un puntero nulo (0 ´ NULL) cuando no pueda encontrar un
                                  a                      o
espacio suficiente de memoria. El tama˜o del espacio lo calcula el propio compilador seg´n el
                                        n                                              u
objeto a ubicar. En esto est´ la mayor diferencia con C. Por otro lado
                            a
      delete pint;                 // libera la memoria conseguida
      delete[] aint;               // requiere [] con arrays
El hecho de que sea un operador hace m´s c´modo su uso ya que tampoco hace falta el incluir
                                         a o
<stdlib.h> con el prototipo (interfaz) de malloc, etc. sino que el propio compilador expande el
operador al estilo de un macro:

      pa = new int;               // (int *) malloc(sizeof(int))
es equivalente al macro:
      #define new(X)       (X *) malloc(sizeof(X))

encarg´ndose el compilador de C++ de estos detalles.
      a

Conversi´n de tipos El operador de conversi´n de tipos de C se ve ampliado en C++ y adem´s
          o                                    o                                               a
se usa con una sintaxis un poco diferente a´n en el caso sencillo de adaptaci´n de tipos sencilla
                                           u                                 o
com´n:
    u
      int num = 33;
      char c1 = (char) num;          // estilo C
      char c2 = char (num);          // estilo C++
pero existen en C++ otros conversores de tipo como son conversi´n de atributo const, reinterpretar
                                                                 o
el significado de los bytes (sin cambio de valores) y conversores entre clases, de una sem´ntica m´s
                                                                                         a       a
compleja de la que se pretende cubrir en esta introducci´n.
                                                          o
5.3    Clases                                                                                    5


5.3.       Clases
    La mayor diferencia entre C y C++ est´ en soporte que el ultimo ofrece para las clases 4 .
                                            a                     ´
Esencialmente una clase es un tipo abstracto de datos: colecci´n de datos junto a operadores para
                                                              o
acceder y modificarlos. Con las clases podremos, pues construir nuevos tipos de datos, que siendo
cuidadosos en su construcci´n, ser´n equivalentes en su trato, con los tipos predefinidos por el
                            o      a
lenguaje. As´ podr´
            ı     ıamos definir una clase Complejo y variables
         Complejo c1, c2, c3;
y, mediante la sobrecarga de operadores que C++ permite, operar correctamente con nuestros
propios operadores de multiplicaci´n, etc´tera
                                  o      e
         c3 = c1 * c2;
adem´s del resto de operaciones que queramos definir sobrecargando los operadores propios del
      a
lenguaje.
     Pero el uso de las clases es a´n m´s interesante desde el punto de vista de la programaci´n
                                    u    a                                                       o
orientada a objetos. Si estamos desarrollando una interfaz de usuario, prob´blemente tengamos
                                                                              a
un objeto panel que puede siembre abrirse, cerrarse, ocultarse, etc. Pero despu´s veremos que
                                                                                    e
un di´logo es un panel m´s otra serie de operaciones como la de interactuar con el teclado. Una
     a                       a
ventana ser´ otro tipo de panel que tendr´ barra de desplazamiento, etc. Luego ser´n todos objetos
            a                              a                                      a
derivados del primero con comportamientos heredados de aqu´l m´s particularidades propias. Si
                                                               e a
lo que estamos desarrollando es una aplicaci´n de contabilidad, podremos tener una clase Cuenta
                                              o
con operaciones comunes como deposito, retiro, etc.
     Las clases facilitan la programaci´n de aplicaciones complejas haciendolas m´s legibles y con-
                                       o                                         a
trolables. Sin embargo el precio es una mayor dificultad de aprendizaje del lenguaje. La progra-
maci´n orientada a objetos es adecuada en aplicaciones medianas a grandes, no tanto en peque˜as
     o                                                                                          n
aplicaciones.

5.4.       Definici´n de clases
                  o
    Definir una clase en C++ es muy parecido a definir una structura en C. En su forma m´s         a
simple, la el aspecto de una clase es id´ntico al de una estructura, usando la palabra class en vez
                                        e
de struct:
         class Complejo {
           float parteReal;
           float parteImaginaria;
         };
dici´ndose que parteReal y parteImaginaria son miembros de la clase Complejo. La convenci´n
    e                                                                                         o
de comenzar los nombres de los tipos con may´sculas es adecuada tambi´n aqu´ pero por supuesto
                                              u                        e      ı,
depende de las normas de estilo del grupo de programadores, no del lenguaje.
      En una terminolog´ m´s general en programaci´n orientada a objetos, los datos miembros
                        ıa a                         o
son los atributos de la clase.
      Una vez definida la clase, su nombre se puede usar para declarar variables como se hac´ con
                                                                                           ıa
las estructuras:
         Complejo c1, c2;
N´tese que el identificador (la etiqueta) sirve en C++ como nombre del tipo y no es necesario
  o
escribir class Complejo c1;.
     A las variables c1 y c2 se les llama instancias de la clase Complejo. Una instancia de
cualquier clase se denomina objeto.
     Los miembros de una estructura son accedidos mediante los operadores . y ->. En una clase,
por otro lado, los miembros est´n ocultos, mientras no se indique lo contrario, o sea, que por
                                a
defecto son inaccesibles, de manera que las siguientes acciones son ilegales:
  4 El   nombre original de C++ era C with Classes: C + C.
5.5    M´todos
        e                                                                                      6


       c1.parteReal = 0.0;          // ilegal, no accesible
       im = c2.parteImaginaria;     // ilegal, no accesible
Decimos que parteReal y parteImaginaria son miembros privados de la clase Complejo.
    Pero si as´ lo queremos, podemos hacer accesibles los miembros de una clase declar´ndolos
              ı                                                                       a
public:
       class Complejo {
       public:
         float parteReal;
         float parteImaginaria;
       };
Podemos mezclar partes p´blicas y privadas de una clase:
                        u
       class Complejo {
       public:
         float parteReal;
       private:
         float parteImaginaria;
       };
N´tese el uso de private para comenzar una secci´n de declaraciones ocultas. Al principio de la
 o                                                o
declaraci´n si no se indica otra cosa se supone impl´
         o                                          ıcitamente la palabra private, por lo que lo
anterior es equivalente a:
       class Complejo {
         float parteImaginaria;
       public:
         float parteReal;
       };

5.5.     M´todos
          e
     Si no son visibles los miembros privados de una clase, ¿c´mo se pueden modificar o ver sus
                                                               o
valores? La respuesta es ingenua: las funciones que necesiten acceder a los datos miembros de una
clase deben estar declarados dentro de la misma clase. Son las funciones miembro o m´todos.
                                                                                        e
     Esta es la t´cnica que C++ utiliza para ver/modificar sus datos miembro. La diferencia entre
                 e
las clases y las estructuras empieza realmente aqu´ para controlar el uso de la informaci´n que
                                                    ı,                                     o
determina el estado de un objeto se deben usar m´todos que son las funciones, v´ de acceso
                                                     e                               ıas,
seguras para controlar y presentar la informaci´n oculta de manera adecuada. As´ un objeto tiene
                                               o                                  ı
un estado interno reconocible por su comportamiento ante las llamadas a sus m´todos.
                                                                                  e
     As´ı:
       class Complejo {
       public:
         void crear(int preal int pimag);
         void print();
       private:
         float parteReal;
         float parteImaginaria;
       };
tienen los m´todos crear y print que son miembros p´blicos, accesibles desde fuera de la clase.
            e                                         u
Para acceder a los m´todos de los objetos se utiliza como para acceder sus datos miembro, la
                     e
notaci´n “punto”:
      o
       c1.crear(0.0, 1.0);        // c1 contiene el n´mero complejo 0 + 1 i
                                                     u
       c1.print();                // imprimir´a 0 + 1 i
                                             ı
5.5    M´todos
        e                                                                                        7


Respecto al estilo empleado en C, estas llamadas son extra˜as ya que se conoce al objeto al que
                                                            n
se llama, no poni´ndolo como par´metro, sino anteponi´ndolos como prefijo.
                  e              a                      e
      No todos los m´todos de una clase tienen que ser p´blicos. Por ejemplo:
                    e                                   u
        class Fraccion {
        public:
          void crear(int num, int denom);
          void print();
        private:
          void reduce();
          int numerador, denominador;
La filosof´ subyacente es la de ofertar (hacer p´blico) s´lo lo necesario e imprescindible para,
          ıa                                      u        o
por un lado no confundir innecesariamente al usuario del objeto y, por otro, evitar errores en la
manipulaci´n de los mismos.
            o
     Hasta ahora s´lo hemos definido los m´todos de las clases y objetos de aqu´llas, pero ¿d´nde
                   o                        e                                 e             o
est´n los algoritmos que realizan tales m´todos?
   a                                      e
     Una posibilidad es definir cada m´todo m´s adelante, fuera de la definici´n de la clase. Por
                                        e       a                            o
ejemplo, en el caso de la funci´n crear de las fracciones:
                               o

        void Fraccion.crear(int num, int denom)
        {
          numerador = num;
          denominador = denom;
          reduce();
        }

N´tese c´mo Fraccion:: precede el nombre de la funci´n. Esta es la notaci´n que C++ emplea
  o      o                                               o                     o
para distinguir funciones ordinarias de los m´todos propios de una clase. Observar que crear tiene
                                              e
acceso directo a los datos miembros de la clase propia. En general esto es as´ ya sean los datos
                                                                                ı
p´blicos o privados.
 u
    En vez de definir posteriormente los m´todos de una clase, se pueden resolver inline sobre la
                                             e
marcha en la propia definici´n de la clase:
                             o
        class Fraccion {
        public:
          void crear(int num, int denom)
            {numerador = num; denominador = denom; reduce();};
          void print();
        private:
          void reduce();
          int numerador, denominador;
esto es conveniente, s´lo si el cuerpo del m´todo es corto.
                       o                    e
     A˜adamos el m´todo mul para multiplicar modificando ´sta por otra fracci´n:
       n             e                                      e               o
        class Fraccion {
        public:
          void crear(int num, int denom);
          void print();
          Fraccion mul(Fraccion f);   // m´todo para multiplicar
                                          e
        private:
          void reduce();
          int numerador, denominador;
       Despu´s tendr´
            e       ıamos que escribir la definici´n (antes escribimos la declaraci´n) de la funci´n
                                                 o                                o              o
mul:
5.6    Constructores y Destructores                                                                8



       Fraccion Fraccion::mul(Fraccion f)
       {
         Fraccion res;

           res.numerador   =   numerador * f.numerador;
           res.denominador = denominador * f.denominador;
           res.reduce();
           return res;
       }

      Inicialmente la funci´n mul puede parecer algo misteriosa: est´ claro cu´l es uno de los mun-
                           o                                        a         a
tiplicandos, el argumento f, pero ¿d´nde est´ el otro? La respuesta est´ en la forma en la que mul
                                     o       a                          a
es llamado:
       f3 = f1.mul(f2);

5.6.       Constructores y Destructores
     Para asegurar que las instancias de una clase se inicializan adecuadamente la clase puede tener
unos funciones especiales denominadas constructores. As´       ımismo la clase puede tener destruc-
tores, funciones que arreglan y dejan las cosas como estaban antes de eliminarse el objeto. Lo
interesante en principio respecto a los constructores y destructores es que son llamados autom´ti-a
camente sin una llamada expl´   ıcita. Hay que tener cuidado con esto.
     Nosotros en nuestro ejemplo, hasta ahora, hemos usado una llamada expl´         ıcita crear para
asignar valores a los atributos. Dada la importancia de la inicializaci´n y la limpieza posterior C++
                                                                       o
soporta el uso de funciones contructores y destructores autom´ticamente llamados en el momento
                                                                 a
de la creaci´n de una nueva instancia de la misma y en el momento de su destrucci´n, en el
            o                                                                                o
momento en que la instancia deja de existir, generalmente esto ultimo ocurrir´ cuando la funci´n
                                                                   ´              a                o
en la que est´ el objeto local deje de existir.
             a
     La sintaxis elegida en C++ es de que toda funci´n con el mismo nombre de la clase que
                                                          o
no devuelve nada (ni void siquiera) es un constructor. Mientras que toda funci´n con el mismo
                                                                                     o
nombre de la clase antecedido de una tilde ~ es un destructor.
     En nuestro ejemplo:
       class Fraccion {
       public:
         Fraccion(int num, int denom)
           {numerador = num; denominador = denom; reduce(); }
       ...
       };
Notemos que el constructor va en la parte p´blica de la clase. Los constructores pueden ser llama-
                                           u
dos expl´ıcitamente como los otros m´todos pero lo m´s interesante de ellos es que son llamados
                                     e                a
impl´
    ıcitamente en el momento de crear una nueva instancia del objeto:
       Fraccion f(7,3);       // declara e inicializa f a 7/3
      Pero ¿qu´ ocurrir´ con la declaraci´n?
              e        ıa                o
       Fraccion muchasFracs[3];
El compilador se quejar´ inmediatamente de que no estamos poniendo los argumentos exigidos
                         ıa
en la llamada impl´
                  ıcita de construcci´n de cada instancia de fracci´n.
                                     o                             o
     Una primera soluci´n a esto (como veremos en §5.7) ser´ crear un constructor m´s sobrecar-
                        o                                   ıa                     a
gando el anterior pero sin par´metros. Como veremos esta es una soluci´n factible, pero la m´s
                              a                                         o                    a
adecuada es la de usar par´metros por defecto:
                            a
5.6   Constructores y Destructores                                                               9


      class Fraccion {
      public:
        Fraccion(int num=0, int denom=1)
          {numerador = num; denominador = denom; reduce(); }
      ...
      };
que, como sabemos permite mucha mayor flexibilidad en las llamadas. Podr´
                                                                       ıamos crear:
      Fraccion    f(7,3);    //   7/3
      Fraccion    f(7);      //   7/1
      Fraccion    f;         //   0/1
      Fraccion    f[10];     //   10 fracciones f[0]..f[9] inicializadas a 0/1
    Los constructores y destructores son especialmente importantes cuando los objetos necesitan
adquirir din´micamente memoria del sistema para almacenar sus datos (atributos) mediante new
            a
y delete.
    Veamos un ejemplo. La clase Cadena puede contener cadenas de caracteres de cualquier lon-
gitud. La podemos definir:
      class Cadena {
      ..
      private:
        char *texto;
        int long;
      };
O sea un puntero a la ristra de caracteres y, optimizando mucho su c´lculo, una variable que recoja
                                                                    a
su longitud.
     La manera m´s adecuada de usar estos objetos ser´ inicializ´ndolos con un valor y posterior-
                 a                                      ıa       a
mente modific´ndolos. Tendr´
             a                ıamos:
      class Cadena {
      public:
        Cadena(const char *s);         // constructor
      private:
        char *texto;
        int long;
      }
Y entonces:

      Cadena::Cadena(const char *s)
      {
        long = strlen(s);
        text = new char[long+1];
        strcpy(text, s);
      }

Despu´s de calcular la longitud de la cadena a la que apunta s el constructor llama al operador
      e
new para conseguir espacio de memoria suficiente para copiar la cadena; finalmente el constructor
copia la cadena en el espacio reci´n conseguido.
                                  e
    Al construir objetos de tipo Cadena har´ ıamos:
      Cadena c1("Hola"), c2("y adi´s.");
                                  o
Para que esta clase tenga alg´n inter´s habr´ l´gicamente que a˜adirle m´todos para modificar y
                             u       e      ıa o                n        e
leer la cadena. As´
                  ımismo podr´ ıamos tener un constructor con valor por defecto nulo:
5.7    Sobrecarga                                                                                10


       class Cadena {
       public:
         Cadena(const char *s = 0);         // constructor
       ...
       }
y despu´s:
       e

       Cadena::Cadena(const char *s)
       {
         if (s == 0) {
           long = 0;
           text = 0;
         } else {
           long = strlen(s);
           text = new char[long+1];
         strcpy(text, s);
         }
       }


     La cuesti´n viene cuando este objeto deje de existir, ya sea porque era local a una funci´n
              o                                                                                 o
o porque se acabe el programa, etc. Entonces habr´ que eliminar la memoria conseguida con
                                                    ıa
new mediante el delete adecuado. C++ aporta un destructor por defecto que si se dice nada lo
unico que hace es liberar la memoria que ocupaban los miembros de la instancia de la clase. Esto
´
es lo usual tambi´n con variables est´ticas. Sin embargo C++ es incapaz de invertir el algoritmo
                 e                   a
de construcci´n empleado para almacenar la cadena de caracteres en el objeto y as´ liberar la
              o                                                                        ı
memoria que usaba. En este caso es pues necesario a˜adir un desctructor expl´
                                                    n                         ıcito a la clase que
ser´ llamado autom´ticamente por C++ cuando el objeto desaparezca:
   a                a
       class Cadena {
       public:
         Cadena(const char *s = 0);
         ~Cadena() { delete[] text; } // destructor
       ...
       }

5.7.      Sobrecarga
     En C++ dos o m´s funciones dentro del mismo ´mbito pueden compartir el mismo nombre.
                     a                               a
Cuando las funciones est´n sobrecargadas es el compilador el que decide en base a la firma
                         a
(signature) de la funci´n cu´l es la que debe ser llamada. S´lo en caso de ambig¨edad el compi-
                       o    a                               o                   u
lador no podr´ decidir. La firma est´ compuesta del nombre, argumentos, tipo y modo de uso de
              ıa                    a
los argumentos y tipo y modo del posible valor devuelto.
     As´ podr´
       ı     ıamos tener:

       void intercambiar(int& a, int &b);
       void intercambiar(char& a, char &b);         // sobrecargada
y despu´s
       e
       intercambiar(a_int, b_int);          // llamar´a a la primera
                                                     ı
       intercambiar(letraa, letrab);        // llamar´a a la segunda
                                                     ı
     Las funciones miembro de una clase tambi´n pueden ser sobrecargadas (lo cual es el uso m´s
                                               e                                                  a
frecuente de la sobrecarga). En el caso de Cadena es m´s f´cil utilizar la sobrecarga que considerar
                                                      a a
aparte el caso de no tener par´metros el constructor:
                              a
5.8    Entrada y salida en C++                                                                    11


       class Cadena {
       public:
         Cadena(const char *s = 0);
         Cadena() {text = 0; long =0; }         // sobrecargada
       ...
       }
     Adem´s de admitir la sobrecarga de funciones, C++ admite la sobrecarga de operadores. De
           a
hecho esto ya es habitual en el caso de los tipos simples predefinidos. Por ejemplo, podemos hacer
3*2 llamando as´ al operador multiplicaci´n de enteros o 3.1*2.0 que llama a un operador distinto,
                 ı                        o
el multiplicador de n´meros reales. La sobrecarga de operadores es particularmente interesante
                      u
para redefinir los operadores usuales ampliando sus posibles argumentos con los nuevos objetos. El
resultado es un aspecto mucho m´s natural de las operaciones que reutilizan nombres u operadores
                                  a
conocidos en vez de tener que recordar multitud de nombres nuevos para cada tipo u objeto nuevo.
     En el caso de Fraccion ser´ mucho m´s f´cil el uso de la multiplicaci´n reemplazando el
                                  ıa           a a                             o
m´todo mul por el operador *. Hacer esto es tan f´cil como substituir el nombre mul por operator*
  e                                               a
en la declaraci´n y definici´n del m´todo:
               o           o         e
       class Fraccion {
       public:
         ...
         Fraccion operator*(Fraccion f);          // m´todo para multiplicar
                                                      e
       private:
         ...
La implementaci´n de operator* es id´ntica que la de mul.
                  o                   e
     Cuando un operador se define como m´todo de una clase uno de sus operandos est´ siempre
                                          e                                          a
impl´ıcito. As´ el operador que acabamos de definir es un operador binario, no unario y cuando
              ı,
escribimos sentencias como:
       f1 * f2;
el compilador nota que f1 es un objeto Fraccion y mira en la clase Fraccion buscando un operador
operator* para convertir la expresi´n en:
                                    o
       f1.operator*(f2);

5.8.      Entrada y salida en C++
     Aunque los programas en C++ pueden utilizar perfectamente <stdio.h>, C++ dispone de una
alternativa a la cl´sica librer´ de entrada y salida. La cabecera m´s importante de la nueva librer´
                   a           ıa                                  a                               ıa
es <iostream.h> que define varias clases, incluyendo istream (canal de entrada, teclado, etc.) y
ostream (canal de salida, pantalla, etc.). Los programas simples que toman su entrada del teclado
y presentan sus resultados en la pantalla usan el objeto cin para la entrada y el objeto cout para
la salida. cin es una instancia de la clase istream y cout es una instancia de la clase ostream.
     Las clases istream y ostream tienen muchos operadores sobrecargados para permitir la salida
y entrada de los tipos est´ndard. La clase istream sobrecarga >> mientras que la clase ostream
                             a
sobrecarga a <<. De esta forma una sesi´n interactiva ser´
                                          o                 ıa:
      cout << "Introducir un n´mero: ";
                              u
      cin >> n;
      cout << "su cuadrado es: " << n*n << endl;

Recordemos que estamos llamando a los operadores ostream::operator<< de la instancia cout
de la clase ostream y al operador istream::operator>> de la instancia cin de la clase istream.
Estos est´n fuertemente recargados, para argumentos de tipo cadena, n´mero, etc´tera.
          a                                                          u         e
     Entre las ventajas de usar la librer´ iostream est´n:
                                         ıa            a
5.8   Entrada y salida en C++                                                                    12


       Podemos ampliar la entrada y salida us´ndo los mismos operadores para las clases que
                                             a
       definamos.
       Es m´s segura ya que no se producen nunca incoherencias entre el tipo de datos esperado en
             a
       el formato de printf o scanf con el de los par´metros pasados. Estas incoherencias llevan
                                                        a
       f´cilmente, en el caso de scanf a graves fallos de funcionamiento.
        a
       Es m´s r´pida ya que la decisi´n de qu´ algoritmo de salida para el tipo hay que usar se toma
            a a                      o       e
       en el momento de compilar y no hay que interpretar el formato en momento de la ejecuci´n.  o
      Hay que decir tambi´n que printf y scanf son m´s flexibles y potentes.
                          e                           a
      La jerarqu´ de clases de la que deriva <iostream> se esquematiza en la Figura 1:
                ıa


                                          ios_base



                                              ios



                                istream                 ostream



                                          iostream
           ifstream                                                        ofstream

        istringstream                                                    ostringstream


                                fstream             stringstream


Figura 1: Jerarqu´ de la que derivan las clases para manipulaci´n de la entrada y salida est´ndares
                 ıa                                            o                            a
<iostream> y de ficheros fstream.




                                                                     Juan Falgueras
                                                      Dpto. Lenguajes y Ciencias de la Computaci´n
                                                                                                o
                                                                 Universidad de M´laga
                                                                                  a
                                                                    Despacho 3.2.32

Weitere ähnliche Inhalte

Was ist angesagt? (18)

Apun c
Apun cApun c
Apun c
 
C(++) programacion en c y c++
C(++)   programacion en c y c++C(++)   programacion en c y c++
C(++) programacion en c y c++
 
Banco de preguntas_modulo_iv
Banco de preguntas_modulo_ivBanco de preguntas_modulo_iv
Banco de preguntas_modulo_iv
 
Curso de programacion en c++ prev
Curso de programacion en c++ prevCurso de programacion en c++ prev
Curso de programacion en c++ prev
 
Lenguaje c 2
Lenguaje c 2Lenguaje c 2
Lenguaje c 2
 
Lenguaje c 2_neo
Lenguaje c 2_neoLenguaje c 2_neo
Lenguaje c 2_neo
 
PROGRAMACION EN C
PROGRAMACION EN CPROGRAMACION EN C
PROGRAMACION EN C
 
Manual C/C++ Jason Martinez
Manual C/C++ Jason MartinezManual C/C++ Jason Martinez
Manual C/C++ Jason Martinez
 
Programación en c++
Programación en c++Programación en c++
Programación en c++
 
Programación en c y c++ prev
Programación en c y c++ prevProgramación en c y c++ prev
Programación en c y c++ prev
 
Programación c++forcsma
Programación c++forcsmaProgramación c++forcsma
Programación c++forcsma
 
Introduccion Al Llenguaje C
Introduccion Al Llenguaje CIntroduccion Al Llenguaje C
Introduccion Al Llenguaje C
 
Programación en c y c++
Programación en c y c++Programación en c y c++
Programación en c y c++
 
Lenguaje c
Lenguaje c Lenguaje c
Lenguaje c
 
Algoritmos
AlgoritmosAlgoritmos
Algoritmos
 
Comparacion lenguajes-de-programacion-c-y-cplusplus
Comparacion lenguajes-de-programacion-c-y-cplusplusComparacion lenguajes-de-programacion-c-y-cplusplus
Comparacion lenguajes-de-programacion-c-y-cplusplus
 
Ova unidad1 introduccionalaprogramacion
Ova unidad1 introduccionalaprogramacionOva unidad1 introduccionalaprogramacion
Ova unidad1 introduccionalaprogramacion
 
Microcontroladores: Programación de microcontroladores tomo 1
Microcontroladores: Programación de microcontroladores tomo 1Microcontroladores: Programación de microcontroladores tomo 1
Microcontroladores: Programación de microcontroladores tomo 1
 

Ähnlich wie Programación orientada a objetos C (20)

mprogintc++.pdf
mprogintc++.pdfmprogintc++.pdf
mprogintc++.pdf
 
CURSO C ++ (1).pdf
CURSO C ++ (1).pdfCURSO C ++ (1).pdf
CURSO C ++ (1).pdf
 
CURSO C ++ (1).pdf
CURSO C ++ (1).pdfCURSO C ++ (1).pdf
CURSO C ++ (1).pdf
 
Mprogintc++ regular
Mprogintc++ regularMprogintc++ regular
Mprogintc++ regular
 
Mprogintc++
Mprogintc++Mprogintc++
Mprogintc++
 
Apuntes de progra c++
Apuntes de progra c++Apuntes de progra c++
Apuntes de progra c++
 
Manual de programacion en lenguaje c++
Manual de programacion en lenguaje c++Manual de programacion en lenguaje c++
Manual de programacion en lenguaje c++
 
Mprogintc++ prog(3)
Mprogintc++ prog(3)Mprogintc++ prog(3)
Mprogintc++ prog(3)
 
Eda1
Eda1Eda1
Eda1
 
Tema2 C++ 2004 2005
Tema2 C++ 2004 2005Tema2 C++ 2004 2005
Tema2 C++ 2004 2005
 
Manual laboratorio de programación#1
Manual laboratorio de programación#1Manual laboratorio de programación#1
Manual laboratorio de programación#1
 
Csharp
CsharpCsharp
Csharp
 
Grupo 201
Grupo 201Grupo 201
Grupo 201
 
Reporte de visual c#(jose gonzalo estrada lopez)
Reporte de visual c#(jose gonzalo estrada lopez)Reporte de visual c#(jose gonzalo estrada lopez)
Reporte de visual c#(jose gonzalo estrada lopez)
 
C++
C++C++
C++
 
C++
C++C++
C++
 
C++
C++C++
C++
 
Manual Básico de Programación en C++
Manual Básico de Programación en C++Manual Básico de Programación en C++
Manual Básico de Programación en C++
 
C++
C++C++
C++
 
C++
C++C++
C++
 

Mehr von Aldo Hernán Zanabria Gálvez

“PERSPECTIVAS DEL DESARROLLO ECONÓMICO REGIONAL EN EL CONTEXTO DEL CAMBIO CLI...
“PERSPECTIVAS DEL DESARROLLO ECONÓMICO REGIONAL EN EL CONTEXTO DEL CAMBIO CLI...“PERSPECTIVAS DEL DESARROLLO ECONÓMICO REGIONAL EN EL CONTEXTO DEL CAMBIO CLI...
“PERSPECTIVAS DEL DESARROLLO ECONÓMICO REGIONAL EN EL CONTEXTO DEL CAMBIO CLI...Aldo Hernán Zanabria Gálvez
 
Organizadores visuales sobre las corrientes contemporaneas aldo zanabria ga...
Organizadores visuales sobre las corrientes contemporaneas   aldo zanabria ga...Organizadores visuales sobre las corrientes contemporaneas   aldo zanabria ga...
Organizadores visuales sobre las corrientes contemporaneas aldo zanabria ga...Aldo Hernán Zanabria Gálvez
 
Resumen final - Seminario Taller TIC Emprede Turismo
Resumen final - Seminario Taller TIC Emprede TurismoResumen final - Seminario Taller TIC Emprede Turismo
Resumen final - Seminario Taller TIC Emprede TurismoAldo Hernán Zanabria Gálvez
 
Clase de Tecnologías de la Información y Comunicaciones
Clase de Tecnologías de la Información y ComunicacionesClase de Tecnologías de la Información y Comunicaciones
Clase de Tecnologías de la Información y ComunicacionesAldo Hernán Zanabria Gálvez
 

Mehr von Aldo Hernán Zanabria Gálvez (20)

“PERSPECTIVAS DEL DESARROLLO ECONÓMICO REGIONAL EN EL CONTEXTO DEL CAMBIO CLI...
“PERSPECTIVAS DEL DESARROLLO ECONÓMICO REGIONAL EN EL CONTEXTO DEL CAMBIO CLI...“PERSPECTIVAS DEL DESARROLLO ECONÓMICO REGIONAL EN EL CONTEXTO DEL CAMBIO CLI...
“PERSPECTIVAS DEL DESARROLLO ECONÓMICO REGIONAL EN EL CONTEXTO DEL CAMBIO CLI...
 
mejorando la web guia de html 5
mejorando la web guia de html 5mejorando la web guia de html 5
mejorando la web guia de html 5
 
Guía de Prácticas word beta.pdf
Guía de Prácticas word beta.pdfGuía de Prácticas word beta.pdf
Guía de Prácticas word beta.pdf
 
emprendimiento en la era del conocimiento.pptx
emprendimiento en la era del conocimiento.pptxemprendimiento en la era del conocimiento.pptx
emprendimiento en la era del conocimiento.pptx
 
Fundamentos de Programación
Fundamentos de ProgramaciónFundamentos de Programación
Fundamentos de Programación
 
Organizadores visuales sobre las corrientes contemporaneas aldo zanabria ga...
Organizadores visuales sobre las corrientes contemporaneas   aldo zanabria ga...Organizadores visuales sobre las corrientes contemporaneas   aldo zanabria ga...
Organizadores visuales sobre las corrientes contemporaneas aldo zanabria ga...
 
didactica
didacticadidactica
didactica
 
Tarea1 aldo zanabria
Tarea1 aldo zanabriaTarea1 aldo zanabria
Tarea1 aldo zanabria
 
Tarea 2 aldo zanabria
Tarea 2 aldo zanabriaTarea 2 aldo zanabria
Tarea 2 aldo zanabria
 
Carolinos del milenio pasado - Puno
Carolinos del milenio pasado - PunoCarolinos del milenio pasado - Puno
Carolinos del milenio pasado - Puno
 
ingenieria de sistemas
ingenieria de sistemasingenieria de sistemas
ingenieria de sistemas
 
Electricidad con recursos renovables
Electricidad con recursos renovablesElectricidad con recursos renovables
Electricidad con recursos renovables
 
Variables
VariablesVariables
Variables
 
Estructura y modelo organizacional estatal
Estructura y modelo organizacional estatal Estructura y modelo organizacional estatal
Estructura y modelo organizacional estatal
 
Calidad de Agua
Calidad de AguaCalidad de Agua
Calidad de Agua
 
Resumen final - Seminario Taller TIC Emprede Turismo
Resumen final - Seminario Taller TIC Emprede TurismoResumen final - Seminario Taller TIC Emprede Turismo
Resumen final - Seminario Taller TIC Emprede Turismo
 
Clase de Tecnologías de la Información y Comunicaciones
Clase de Tecnologías de la Información y ComunicacionesClase de Tecnologías de la Información y Comunicaciones
Clase de Tecnologías de la Información y Comunicaciones
 
Plan de Trabajo Integración de la Mujer
Plan de Trabajo Integración de la MujerPlan de Trabajo Integración de la Mujer
Plan de Trabajo Integración de la Mujer
 
peritaciones y tasación puno
peritaciones y tasación punoperitaciones y tasación puno
peritaciones y tasación puno
 
producción en la empresa turística
producción en la empresa turísticaproducción en la empresa turística
producción en la empresa turística
 

Programación orientada a objetos C

  • 1. Programaci´n Modular. ETSIT. 1o C. o Apuntes del profesor Juan Falgueras. Curso 2001/02 versi´n: 28 de abril de 2003 o 5 Programaci´n orientada a objetos o Contenido 5. Programaci´n orientada a objetos o 1 5.1. Introducci´n a C++ . . . . . . . . o . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 5.2. Diferencias entre C y C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 5.3. Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5.4. Definici´n de clases . . . . . . . . o . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5.5. M´todos . . . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 5.6. Constructores y Destructores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 5.7. Sobrecarga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 5.8. Entrada y salida en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 5. Programaci´n orientada a objetos o 5.1. Introducci´n a C++ o El lenguaje C++ fue desarrollado por Bjarne Stroustrup1 de AT&T Bell Laboratories durante los 80. El autor expandi´ enormemente el lenguaje C para dar soporte a principios de dise˜o m´s o n a modernos. La diferencia m´s importante entre C y C++ est´ en el soporte para clases, pero son a a fundamentales tambi´n: e 1. Sobrecarga de operadores, que hace posible dar significados a˜adidos a los operadores tradi- n cionales. 2. Plantillas (templates), que permiten escribir pre-c´digo muy reutilizable sin concretar a´n o u elementos variables. 3. Gesti´n de excepciones, uniformando dando soporte a la detecci´n y respuesta a los errores o o de ejecuci´n. o Sin embargo uno de los objetivos que se propuso el autor fue el de mantener a´n compatibilidad con u el lenguaje germinal C en lo posible. As´ todas las posibilidades del est´ndard C est´n igualmente ı a a presentes en C++. Esto, sin embargo no significa que todos los programas escritos en C compilen en C++; existen restricciones en C++ respecto a C sobre todo en aras de una mayor seguridad. 5.2. Diferencias entre C y C++ Adem´s de las grandes novedades relativas a la programaci´n orientada a objetos (clases, a o sobrecarga, derivaci´n, funciones virtuales, plantillas y gesti´n de excepciones), C++ a˜ade a C o o n peque˜as diferencias que conviene conocer. n 1 http://www.research.att.com/~bs/homepage.html
  • 2. 5.2 Diferencias entre C y C++ 2 Comentarios C++ tiene comentarios de ´mbito limitado a una s´la l´ a o ınea, frente a C que no acaba el comentario al terminar la l´ ınea. Como ya habremos visto, los comentarios de C++ empiezan con // y terminan con el final de la l´ ınea. Esto no quiere decir, por otra parte, que no se puedan utilizar tambi´n la t´cnica de comentarios de C. e e Etiquetas frente a nombres de tipo Los identificadores de estructuras, uniones y enumerados bastan para definir en C++ el propio tipo sin necesidad de crear un tipo con typedef. As´ en vez ı de typedef struct {int numerador, denominador;} Fracc; basta (en C++) escribir: struct Fracc {int numerador, denominador;}; pudi´ndose desde entonces definir estructuras de tipo Fracc;. e Funciones sin argumentos No hay necesidad de usar la palabra void cuando se definen fun- ciones sin argumentos: int leeInt(void); // en C int leeInt(); // es suficiente en C++ Argumentos con valor por defecto C++ permite que un argumento tome un valor por defecto cuando no se especifique un valor real en la llamada. Por ejemplo: void wrLn(int n = 1) { // tiene un par´metro que toma por defecto el valor 1 a while (n-- > 0) putchar(’n’); } puede ser llamado con o sin par´metros a wrLn(3); // escribir´a tres saltos de l´nea ı ı wrLn(); // escribir´a uno ı Si se define la funci´n int a(int a=1, int b=2, int c), ¿qu´ significa a(4,5)? Podr´ significar o e ıa varias cosas con lo que los argumentos con valores por defecto siempre deben estar juntos al final de la lista de argumentos formales. int a(int c, int a=1, int b=2) hace que a(1), ´ a(1,2) o ´ a(1,2,3) sean todos no-ambiguos.2 o Funciones inline El lenguaje C explot´ enormemente el uso del preprocesado l´xico. El prepro- o e cesador act´a antes que el compilador substituyendo sint´cticamente los elementos #define’dos u a por su equivalente, sin que el preprocesador interprete el significado sino s´lo los tokens l´xicos que o e intervienen. Esta idea proven´ de los antiguos lenguajes ensambladores de los que C era cercano ıa heredero. La #define’ci´n de s´ o ımbolos es s´lo el mecanismo m´s simple; cpp, el preprocesador, o a previo a C, permite la expansi´n de macros. Una macro es un s´ o ımbolo con argumentos. Entonces se sustituye el s´ ımbolo por la expresi´n y los argumentos se intercalan en la expresi´n all´ d´nde o o ı o se indique. La potencia sint´ctica del preprocesado de macros de C tiene sin embargo algunos puntos a oscuros. Imagin´monos un macro e #define cuadrado(x) (x*x) 2 En otros lenguajes es posible poner nombres a los argumentos en la propia llamada, a(1, c=>3) permitir´ que ıa b tomase libremente su valor por defecto. Esto ayuda a manejar funciones con muchos argumentos superfluos.
  • 3. 5.2 Diferencias entre C y C++ 3 que significa que all´ en el fuente en C d´nde aparezca cuadrado(algo) lo debe substituir por ı o cuadrado(algo*algo). Insistimos, el preprocesador no entra en interpretar lo que signifique nin- guna de las expresiones anteriores, eso se lo deja al compilador que recoje el resultado del prepro- cesado. Ve´mosla en acci´n en diversas ocasiones como: a o void f(double d, int i) { r = cuadrado(d); // bien r = cuadrado(i++); // mal: significa (i++*i++) r = cuadrado(d+1); // mal: significa (d+1*d+1); que es, (d+d+1) // ... } El problema de “d+1” se resuelve mediante el a˜adido de par´ntesis en el macro: n e #define cuadrado(x) ((x)*(x)) /* mejor */ pero el de “i++” no tiene soluci´n en C. C++ a˜ade un tipo de “funciones” que se expanden o n (sint´cticamente) en el momento de ser utilizadas. As´ no es un preprocesador, el preprocesador de a ı C, que no tiene en cuenta la sem´ntica de las operaciones C, el que se encarga de expandir estas a construcciones sint´cticas sino el propio compilador en C++ el que expande de manera correcta a estos macros, teniendo sobre todo en cuenta la manipulaci´n correcta de los argumentos. o inline int icuadrado(int x) {return x * x;} El hecho de que sea el propio compilador el que expanda el cuerpo de la funci´n obliga, sin embargo, o a que el tipo del argumento sea conocido, algo que C++ no puede resolver excepto con el uso de plantillas, que son, sin embargo, inadecuadas para este prop´sito. o Paso de par´metros por referencia En C todos los par´metros siempre son pasados por a a valor, esto es, copiando el par´metro que se ponga en la llamada, par´metro actual, a la variable a a receptora en el par´metro formal ya como variable local del procedimiento. Esto nos obliga a a utilizar a poner las direcciones de las variables (& en los par´metros de la llamada y a apuntar a mediante esas direcciones (*) dentro del procedimiento para poder modificar los valores de las variables pasadas. void intercambia(int *a, int *b) { int temp= *a; *a = *b; *b = temp; } y es llamado mediante intercambia(&i, &j);. O la tan famosa scanf que require referencias a los elementos a leer. Por ejemplo: for (i=0; i<N; i++) scanf("%d", &a[i]); Esta t´cnica es inc´moda e insegura3 . C++ detalla y permite un mayor control del paso de e o par´metros a˜adiendo la posibilidad del paso de par´metros por referencia. En este caso, el par´me- a n a a tro actual no necesita llevar el indicador de que se va modificar (&) sino que es en el par´metro a 3 Aunque esta t´cnica es inc´moda, tambi´n hay que decir que es m´s expl´ e o e a ıcita en cuanto a la peligrosidad de uso de este tipo de modificaciones no locales. Es m´s expl´ a ıcita porque manifiesta el cambio mediante el signo * en el procedimiento. Sin embargo respecto a los arrays el tema es m´s oscuro y el cambio se produce siempre ya a que el propio nombre del array es la direcci´n del bloque de datos del mismo. Recordemos que en C los par´ntesis o e cuadrados [] act´ an como operadores de indirecci´n siempre: x[i] equivale *(x+i) u o
  • 4. 5.2 Diferencias entre C y C++ 4 formal de la declaraci´n del procedimiento donde se indica que se va a recibir al par´metro por o a referencia. Esto es mucho m´s c´mo en muchos casos y es preferido por la mayor´ de los pro- a o ıa gramadores. Es un mecanismo a˜adido. El compilador se encarga de los aspectos ‘sucios’ de la n modificaci´n del par´metro actual. No hay que usar & cada vez que se llame en el par´metro o a a formal ni * en la funci´n cada vez que se quiera acceder al valor del par´metro. En el caso del o a procedimiento de intercambio anterior escribiremos: void intercambia(int& a, int& b) { int temp= a; a = b; b = temp; } y es llamado mediante intercambia(i, j);. N´tese que aparece una nueva notaci´n: se declara o o un par´metro que es una referencia a un tipo de elemento. C carece de las posibilidades de la a referencia y por ello usa los punteros siempre. Operadores new y delete En vez de las funciones malloc, calloc, realloc y free, aunque C++ las puede utilizar, C++ introduce un operador (y por lo tanto una sintaxis diferente) para adquirir nuevos bloques de memoria y/o crear nuevos objetos en ella. Por ejemplo: int *pint, *pin2, *aint; pint = new int; // adquiere memoria adecuada para un entero pin2 = new int(33); // .. e inicializa su contenido a 33 aint = new int[10]; // ´dem para un array de 10 enteros ı Como es de esperar, new devolver´ un puntero nulo (0 ´ NULL) cuando no pueda encontrar un a o espacio suficiente de memoria. El tama˜o del espacio lo calcula el propio compilador seg´n el n u objeto a ubicar. En esto est´ la mayor diferencia con C. Por otro lado a delete pint; // libera la memoria conseguida delete[] aint; // requiere [] con arrays El hecho de que sea un operador hace m´s c´modo su uso ya que tampoco hace falta el incluir a o <stdlib.h> con el prototipo (interfaz) de malloc, etc. sino que el propio compilador expande el operador al estilo de un macro: pa = new int; // (int *) malloc(sizeof(int)) es equivalente al macro: #define new(X) (X *) malloc(sizeof(X)) encarg´ndose el compilador de C++ de estos detalles. a Conversi´n de tipos El operador de conversi´n de tipos de C se ve ampliado en C++ y adem´s o o a se usa con una sintaxis un poco diferente a´n en el caso sencillo de adaptaci´n de tipos sencilla u o com´n: u int num = 33; char c1 = (char) num; // estilo C char c2 = char (num); // estilo C++ pero existen en C++ otros conversores de tipo como son conversi´n de atributo const, reinterpretar o el significado de los bytes (sin cambio de valores) y conversores entre clases, de una sem´ntica m´s a a compleja de la que se pretende cubrir en esta introducci´n. o
  • 5. 5.3 Clases 5 5.3. Clases La mayor diferencia entre C y C++ est´ en soporte que el ultimo ofrece para las clases 4 . a ´ Esencialmente una clase es un tipo abstracto de datos: colecci´n de datos junto a operadores para o acceder y modificarlos. Con las clases podremos, pues construir nuevos tipos de datos, que siendo cuidadosos en su construcci´n, ser´n equivalentes en su trato, con los tipos predefinidos por el o a lenguaje. As´ podr´ ı ıamos definir una clase Complejo y variables Complejo c1, c2, c3; y, mediante la sobrecarga de operadores que C++ permite, operar correctamente con nuestros propios operadores de multiplicaci´n, etc´tera o e c3 = c1 * c2; adem´s del resto de operaciones que queramos definir sobrecargando los operadores propios del a lenguaje. Pero el uso de las clases es a´n m´s interesante desde el punto de vista de la programaci´n u a o orientada a objetos. Si estamos desarrollando una interfaz de usuario, prob´blemente tengamos a un objeto panel que puede siembre abrirse, cerrarse, ocultarse, etc. Pero despu´s veremos que e un di´logo es un panel m´s otra serie de operaciones como la de interactuar con el teclado. Una a a ventana ser´ otro tipo de panel que tendr´ barra de desplazamiento, etc. Luego ser´n todos objetos a a a derivados del primero con comportamientos heredados de aqu´l m´s particularidades propias. Si e a lo que estamos desarrollando es una aplicaci´n de contabilidad, podremos tener una clase Cuenta o con operaciones comunes como deposito, retiro, etc. Las clases facilitan la programaci´n de aplicaciones complejas haciendolas m´s legibles y con- o a trolables. Sin embargo el precio es una mayor dificultad de aprendizaje del lenguaje. La progra- maci´n orientada a objetos es adecuada en aplicaciones medianas a grandes, no tanto en peque˜as o n aplicaciones. 5.4. Definici´n de clases o Definir una clase en C++ es muy parecido a definir una structura en C. En su forma m´s a simple, la el aspecto de una clase es id´ntico al de una estructura, usando la palabra class en vez e de struct: class Complejo { float parteReal; float parteImaginaria; }; dici´ndose que parteReal y parteImaginaria son miembros de la clase Complejo. La convenci´n e o de comenzar los nombres de los tipos con may´sculas es adecuada tambi´n aqu´ pero por supuesto u e ı, depende de las normas de estilo del grupo de programadores, no del lenguaje. En una terminolog´ m´s general en programaci´n orientada a objetos, los datos miembros ıa a o son los atributos de la clase. Una vez definida la clase, su nombre se puede usar para declarar variables como se hac´ con ıa las estructuras: Complejo c1, c2; N´tese que el identificador (la etiqueta) sirve en C++ como nombre del tipo y no es necesario o escribir class Complejo c1;. A las variables c1 y c2 se les llama instancias de la clase Complejo. Una instancia de cualquier clase se denomina objeto. Los miembros de una estructura son accedidos mediante los operadores . y ->. En una clase, por otro lado, los miembros est´n ocultos, mientras no se indique lo contrario, o sea, que por a defecto son inaccesibles, de manera que las siguientes acciones son ilegales: 4 El nombre original de C++ era C with Classes: C + C.
  • 6. 5.5 M´todos e 6 c1.parteReal = 0.0; // ilegal, no accesible im = c2.parteImaginaria; // ilegal, no accesible Decimos que parteReal y parteImaginaria son miembros privados de la clase Complejo. Pero si as´ lo queremos, podemos hacer accesibles los miembros de una clase declar´ndolos ı a public: class Complejo { public: float parteReal; float parteImaginaria; }; Podemos mezclar partes p´blicas y privadas de una clase: u class Complejo { public: float parteReal; private: float parteImaginaria; }; N´tese el uso de private para comenzar una secci´n de declaraciones ocultas. Al principio de la o o declaraci´n si no se indica otra cosa se supone impl´ o ıcitamente la palabra private, por lo que lo anterior es equivalente a: class Complejo { float parteImaginaria; public: float parteReal; }; 5.5. M´todos e Si no son visibles los miembros privados de una clase, ¿c´mo se pueden modificar o ver sus o valores? La respuesta es ingenua: las funciones que necesiten acceder a los datos miembros de una clase deben estar declarados dentro de la misma clase. Son las funciones miembro o m´todos. e Esta es la t´cnica que C++ utiliza para ver/modificar sus datos miembro. La diferencia entre e las clases y las estructuras empieza realmente aqu´ para controlar el uso de la informaci´n que ı, o determina el estado de un objeto se deben usar m´todos que son las funciones, v´ de acceso e ıas, seguras para controlar y presentar la informaci´n oculta de manera adecuada. As´ un objeto tiene o ı un estado interno reconocible por su comportamiento ante las llamadas a sus m´todos. e As´ı: class Complejo { public: void crear(int preal int pimag); void print(); private: float parteReal; float parteImaginaria; }; tienen los m´todos crear y print que son miembros p´blicos, accesibles desde fuera de la clase. e u Para acceder a los m´todos de los objetos se utiliza como para acceder sus datos miembro, la e notaci´n “punto”: o c1.crear(0.0, 1.0); // c1 contiene el n´mero complejo 0 + 1 i u c1.print(); // imprimir´a 0 + 1 i ı
  • 7. 5.5 M´todos e 7 Respecto al estilo empleado en C, estas llamadas son extra˜as ya que se conoce al objeto al que n se llama, no poni´ndolo como par´metro, sino anteponi´ndolos como prefijo. e a e No todos los m´todos de una clase tienen que ser p´blicos. Por ejemplo: e u class Fraccion { public: void crear(int num, int denom); void print(); private: void reduce(); int numerador, denominador; La filosof´ subyacente es la de ofertar (hacer p´blico) s´lo lo necesario e imprescindible para, ıa u o por un lado no confundir innecesariamente al usuario del objeto y, por otro, evitar errores en la manipulaci´n de los mismos. o Hasta ahora s´lo hemos definido los m´todos de las clases y objetos de aqu´llas, pero ¿d´nde o e e o est´n los algoritmos que realizan tales m´todos? a e Una posibilidad es definir cada m´todo m´s adelante, fuera de la definici´n de la clase. Por e a o ejemplo, en el caso de la funci´n crear de las fracciones: o void Fraccion.crear(int num, int denom) { numerador = num; denominador = denom; reduce(); } N´tese c´mo Fraccion:: precede el nombre de la funci´n. Esta es la notaci´n que C++ emplea o o o o para distinguir funciones ordinarias de los m´todos propios de una clase. Observar que crear tiene e acceso directo a los datos miembros de la clase propia. En general esto es as´ ya sean los datos ı p´blicos o privados. u En vez de definir posteriormente los m´todos de una clase, se pueden resolver inline sobre la e marcha en la propia definici´n de la clase: o class Fraccion { public: void crear(int num, int denom) {numerador = num; denominador = denom; reduce();}; void print(); private: void reduce(); int numerador, denominador; esto es conveniente, s´lo si el cuerpo del m´todo es corto. o e A˜adamos el m´todo mul para multiplicar modificando ´sta por otra fracci´n: n e e o class Fraccion { public: void crear(int num, int denom); void print(); Fraccion mul(Fraccion f); // m´todo para multiplicar e private: void reduce(); int numerador, denominador; Despu´s tendr´ e ıamos que escribir la definici´n (antes escribimos la declaraci´n) de la funci´n o o o mul:
  • 8. 5.6 Constructores y Destructores 8 Fraccion Fraccion::mul(Fraccion f) { Fraccion res; res.numerador = numerador * f.numerador; res.denominador = denominador * f.denominador; res.reduce(); return res; } Inicialmente la funci´n mul puede parecer algo misteriosa: est´ claro cu´l es uno de los mun- o a a tiplicandos, el argumento f, pero ¿d´nde est´ el otro? La respuesta est´ en la forma en la que mul o a a es llamado: f3 = f1.mul(f2); 5.6. Constructores y Destructores Para asegurar que las instancias de una clase se inicializan adecuadamente la clase puede tener unos funciones especiales denominadas constructores. As´ ımismo la clase puede tener destruc- tores, funciones que arreglan y dejan las cosas como estaban antes de eliminarse el objeto. Lo interesante en principio respecto a los constructores y destructores es que son llamados autom´ti-a camente sin una llamada expl´ ıcita. Hay que tener cuidado con esto. Nosotros en nuestro ejemplo, hasta ahora, hemos usado una llamada expl´ ıcita crear para asignar valores a los atributos. Dada la importancia de la inicializaci´n y la limpieza posterior C++ o soporta el uso de funciones contructores y destructores autom´ticamente llamados en el momento a de la creaci´n de una nueva instancia de la misma y en el momento de su destrucci´n, en el o o momento en que la instancia deja de existir, generalmente esto ultimo ocurrir´ cuando la funci´n ´ a o en la que est´ el objeto local deje de existir. a La sintaxis elegida en C++ es de que toda funci´n con el mismo nombre de la clase que o no devuelve nada (ni void siquiera) es un constructor. Mientras que toda funci´n con el mismo o nombre de la clase antecedido de una tilde ~ es un destructor. En nuestro ejemplo: class Fraccion { public: Fraccion(int num, int denom) {numerador = num; denominador = denom; reduce(); } ... }; Notemos que el constructor va en la parte p´blica de la clase. Los constructores pueden ser llama- u dos expl´ıcitamente como los otros m´todos pero lo m´s interesante de ellos es que son llamados e a impl´ ıcitamente en el momento de crear una nueva instancia del objeto: Fraccion f(7,3); // declara e inicializa f a 7/3 Pero ¿qu´ ocurrir´ con la declaraci´n? e ıa o Fraccion muchasFracs[3]; El compilador se quejar´ inmediatamente de que no estamos poniendo los argumentos exigidos ıa en la llamada impl´ ıcita de construcci´n de cada instancia de fracci´n. o o Una primera soluci´n a esto (como veremos en §5.7) ser´ crear un constructor m´s sobrecar- o ıa a gando el anterior pero sin par´metros. Como veremos esta es una soluci´n factible, pero la m´s a o a adecuada es la de usar par´metros por defecto: a
  • 9. 5.6 Constructores y Destructores 9 class Fraccion { public: Fraccion(int num=0, int denom=1) {numerador = num; denominador = denom; reduce(); } ... }; que, como sabemos permite mucha mayor flexibilidad en las llamadas. Podr´ ıamos crear: Fraccion f(7,3); // 7/3 Fraccion f(7); // 7/1 Fraccion f; // 0/1 Fraccion f[10]; // 10 fracciones f[0]..f[9] inicializadas a 0/1 Los constructores y destructores son especialmente importantes cuando los objetos necesitan adquirir din´micamente memoria del sistema para almacenar sus datos (atributos) mediante new a y delete. Veamos un ejemplo. La clase Cadena puede contener cadenas de caracteres de cualquier lon- gitud. La podemos definir: class Cadena { .. private: char *texto; int long; }; O sea un puntero a la ristra de caracteres y, optimizando mucho su c´lculo, una variable que recoja a su longitud. La manera m´s adecuada de usar estos objetos ser´ inicializ´ndolos con un valor y posterior- a ıa a mente modific´ndolos. Tendr´ a ıamos: class Cadena { public: Cadena(const char *s); // constructor private: char *texto; int long; } Y entonces: Cadena::Cadena(const char *s) { long = strlen(s); text = new char[long+1]; strcpy(text, s); } Despu´s de calcular la longitud de la cadena a la que apunta s el constructor llama al operador e new para conseguir espacio de memoria suficiente para copiar la cadena; finalmente el constructor copia la cadena en el espacio reci´n conseguido. e Al construir objetos de tipo Cadena har´ ıamos: Cadena c1("Hola"), c2("y adi´s."); o Para que esta clase tenga alg´n inter´s habr´ l´gicamente que a˜adirle m´todos para modificar y u e ıa o n e leer la cadena. As´ ımismo podr´ ıamos tener un constructor con valor por defecto nulo:
  • 10. 5.7 Sobrecarga 10 class Cadena { public: Cadena(const char *s = 0); // constructor ... } y despu´s: e Cadena::Cadena(const char *s) { if (s == 0) { long = 0; text = 0; } else { long = strlen(s); text = new char[long+1]; strcpy(text, s); } } La cuesti´n viene cuando este objeto deje de existir, ya sea porque era local a una funci´n o o o porque se acabe el programa, etc. Entonces habr´ que eliminar la memoria conseguida con ıa new mediante el delete adecuado. C++ aporta un destructor por defecto que si se dice nada lo unico que hace es liberar la memoria que ocupaban los miembros de la instancia de la clase. Esto ´ es lo usual tambi´n con variables est´ticas. Sin embargo C++ es incapaz de invertir el algoritmo e a de construcci´n empleado para almacenar la cadena de caracteres en el objeto y as´ liberar la o ı memoria que usaba. En este caso es pues necesario a˜adir un desctructor expl´ n ıcito a la clase que ser´ llamado autom´ticamente por C++ cuando el objeto desaparezca: a a class Cadena { public: Cadena(const char *s = 0); ~Cadena() { delete[] text; } // destructor ... } 5.7. Sobrecarga En C++ dos o m´s funciones dentro del mismo ´mbito pueden compartir el mismo nombre. a a Cuando las funciones est´n sobrecargadas es el compilador el que decide en base a la firma a (signature) de la funci´n cu´l es la que debe ser llamada. S´lo en caso de ambig¨edad el compi- o a o u lador no podr´ decidir. La firma est´ compuesta del nombre, argumentos, tipo y modo de uso de ıa a los argumentos y tipo y modo del posible valor devuelto. As´ podr´ ı ıamos tener: void intercambiar(int& a, int &b); void intercambiar(char& a, char &b); // sobrecargada y despu´s e intercambiar(a_int, b_int); // llamar´a a la primera ı intercambiar(letraa, letrab); // llamar´a a la segunda ı Las funciones miembro de una clase tambi´n pueden ser sobrecargadas (lo cual es el uso m´s e a frecuente de la sobrecarga). En el caso de Cadena es m´s f´cil utilizar la sobrecarga que considerar a a aparte el caso de no tener par´metros el constructor: a
  • 11. 5.8 Entrada y salida en C++ 11 class Cadena { public: Cadena(const char *s = 0); Cadena() {text = 0; long =0; } // sobrecargada ... } Adem´s de admitir la sobrecarga de funciones, C++ admite la sobrecarga de operadores. De a hecho esto ya es habitual en el caso de los tipos simples predefinidos. Por ejemplo, podemos hacer 3*2 llamando as´ al operador multiplicaci´n de enteros o 3.1*2.0 que llama a un operador distinto, ı o el multiplicador de n´meros reales. La sobrecarga de operadores es particularmente interesante u para redefinir los operadores usuales ampliando sus posibles argumentos con los nuevos objetos. El resultado es un aspecto mucho m´s natural de las operaciones que reutilizan nombres u operadores a conocidos en vez de tener que recordar multitud de nombres nuevos para cada tipo u objeto nuevo. En el caso de Fraccion ser´ mucho m´s f´cil el uso de la multiplicaci´n reemplazando el ıa a a o m´todo mul por el operador *. Hacer esto es tan f´cil como substituir el nombre mul por operator* e a en la declaraci´n y definici´n del m´todo: o o e class Fraccion { public: ... Fraccion operator*(Fraccion f); // m´todo para multiplicar e private: ... La implementaci´n de operator* es id´ntica que la de mul. o e Cuando un operador se define como m´todo de una clase uno de sus operandos est´ siempre e a impl´ıcito. As´ el operador que acabamos de definir es un operador binario, no unario y cuando ı, escribimos sentencias como: f1 * f2; el compilador nota que f1 es un objeto Fraccion y mira en la clase Fraccion buscando un operador operator* para convertir la expresi´n en: o f1.operator*(f2); 5.8. Entrada y salida en C++ Aunque los programas en C++ pueden utilizar perfectamente <stdio.h>, C++ dispone de una alternativa a la cl´sica librer´ de entrada y salida. La cabecera m´s importante de la nueva librer´ a ıa a ıa es <iostream.h> que define varias clases, incluyendo istream (canal de entrada, teclado, etc.) y ostream (canal de salida, pantalla, etc.). Los programas simples que toman su entrada del teclado y presentan sus resultados en la pantalla usan el objeto cin para la entrada y el objeto cout para la salida. cin es una instancia de la clase istream y cout es una instancia de la clase ostream. Las clases istream y ostream tienen muchos operadores sobrecargados para permitir la salida y entrada de los tipos est´ndard. La clase istream sobrecarga >> mientras que la clase ostream a sobrecarga a <<. De esta forma una sesi´n interactiva ser´ o ıa: cout << "Introducir un n´mero: "; u cin >> n; cout << "su cuadrado es: " << n*n << endl; Recordemos que estamos llamando a los operadores ostream::operator<< de la instancia cout de la clase ostream y al operador istream::operator>> de la instancia cin de la clase istream. Estos est´n fuertemente recargados, para argumentos de tipo cadena, n´mero, etc´tera. a u e Entre las ventajas de usar la librer´ iostream est´n: ıa a
  • 12. 5.8 Entrada y salida en C++ 12 Podemos ampliar la entrada y salida us´ndo los mismos operadores para las clases que a definamos. Es m´s segura ya que no se producen nunca incoherencias entre el tipo de datos esperado en a el formato de printf o scanf con el de los par´metros pasados. Estas incoherencias llevan a f´cilmente, en el caso de scanf a graves fallos de funcionamiento. a Es m´s r´pida ya que la decisi´n de qu´ algoritmo de salida para el tipo hay que usar se toma a a o e en el momento de compilar y no hay que interpretar el formato en momento de la ejecuci´n. o Hay que decir tambi´n que printf y scanf son m´s flexibles y potentes. e a La jerarqu´ de clases de la que deriva <iostream> se esquematiza en la Figura 1: ıa ios_base ios istream ostream iostream ifstream ofstream istringstream ostringstream fstream stringstream Figura 1: Jerarqu´ de la que derivan las clases para manipulaci´n de la entrada y salida est´ndares ıa o a <iostream> y de ficheros fstream. Juan Falgueras Dpto. Lenguajes y Ciencias de la Computaci´n o Universidad de M´laga a Despacho 3.2.32