Tabla de contenidos
Como C++ está basado en C, debería estar familiarizado con la sintaxis de C para poder programar en C++, del mismo modo que debería tener una fluidez razonable en álgebra para poder hacer cálculos.
Si nunca antes ha visto C, este capítulo le dará una buena base sobre el estilo de C usado en C++. Si está familiarizado con el estilo de C descrito en la primera edición de Kernighan & Ritchie (también llamado K&R) encontrará algunas características nuevas o diferentes tanto en C++ como en el estándar C. Si está familiarizado con el estándar C debería echar un vistazo al capítulo en busca de las características particulares de C++. Note que hay algunas características fundamentales de C++ que se introducen aquí, que son ideas básicas parecidas a características de C o a menudo modificaciones en el modo en que C hace las cosas. Las características más sofisticadas de C++ se explicarán en capítulos posteriores
Este capítulo trata por encima las construcciones de C e introduce algunas construcciones básicas de C++, suponiendo que tiene alguna experiencia programando en otro lenguaje. En el CD-ROM que acompaña a este libro hay una introducción más suave a C, titulada Thinking in C: Foundations for Java & C++ de Chuck Alison (publicada por MidView, Inc. y disponible también en www.MindView.net). Se trata de un seminario en CD-ROM cuyo objetivo es guiarle cuidadosamente a través de los fundamentos del lenguaje C. Se concentra en el conceptos necesarios para permitirle pasarse a C++ o a Java, en lugar de intentar convertirle en un experto en todos los oscuros recovecos de C (una de las razones para usar un lenguaje de alto nivel como C++ o Java es precisamente evitar muchos de estos recovecos). También contiene ejercicios y soluciones guiadas. Tenga presente que este capítulo va después del CD Thinking in C, el CD no reemplaza a este capítulo, sino que debería tomarse como una preparación para este capítulo y para el libro.
En el antiguo C (previo al estándar), se podía invocar una función con cualquier número y tipo de argumentos sin que el compilador se quejase. Todo parecía ir bien hasta que ejecutabas el programa. El programa acababa con resultados misteriosos (o peor, el programa fallaba) sin ninguna pista del motivo. La falta de ayuda acerca del paso de argumentos y los enigmáticos bugs que resultaban es, probablemente, la causa de que C se considerase «un lenguaje ensamblador de alto nivel». Los programadores de pre-Estándar C simplemente se adaptaron.
C y C++ Estándar usan una característica llamada prototipado de funciones. Con esta herramienta se han de describir los tipos de argumentos al declarar y definir una función. Esta descripción es el «prototipo». Cuando la función es llamada, el compilador usa el prototipo para asegurar que los argumentos pasados son los apropiados, y que el valor retornado es tratado correctamente. Si el programador comete un error al llamar a la función, el compilador detecta el error.
Esencialmente, aprendió sobre prototipado de funciones (sin llamarlas de ese modo) en el capítulo previo, ya que la forma de declararlas en C++ requiere de un prototipado apropiado. En un prototipo de función, la lista de argumentos contiene los tipos de argumentos que se deben pasar a la función y (opcionalmente para la declaración), identificadores para los argumentos. El orden y tipo de los argumentos debe coincidir en la declaración, definición y llamada a la función. A continuación se muestra un ejemplo de un prototipo de función en una declaración:
int translate(float x, float y, float z);
No se puede usar la misma sintaxis para declarar los argumentos en
el prototipo de una función que en las definiciones ordinarias de
variables. Esto significa que no se puede escribir: float x, y,
z.
Se debe indicar el tipo de cada argumento. En una declaración
de función, lo siguiente también es correcto:
int translate(float, float, float);
Ya que el compilador no hace más que chequear los tipos cuando se invoca la función, los identificadores se incluyen solamente para mejorar la claridad del código cuando alguien lo está leyendo.
En la definición de la función, los nombres son necesarios ya que los argumentos son referenciados dentro de la función:
int translate(float x, float y, float z) { x = y = z; // ... }
Esta regla sólo se aplica a C. En C++, un argumento puede no tener nombrado en la lista de argumentos de la definición de la función. Como no tiene nombre, no se puede utilizar en el cuerpo de la función, por supuesto. Los argumentos sin nombre se permiten para dar al programador una manera de «reservar espacio en la lista de argumentos». De cualquier modo, la persona que crea la función aún así debe llamar a la función con los parámetros apropiados. Sin embargo, la persona que crea la función puede utilizar el argumento en el futuro sin forzar una modificación en el código que llama a la función. Esta opción de ignorar un argumento en la lista también es posible si se indica el nombre, pero siempre aparecería un molesto mensaje de advertencia, informando que el valor no se utiliza, cada vez que se compila la función. La advertencia desaparece si se quita el nombre del argumento.
C y C++ tienen otras dos maneras de declarar una lista de
argumentos. Si se tiene una lista de argumentos vacía, se puede
declarar esta como func()
en C++, lo que
indica al compilador que hay exactamente cero argumentos. Hay que
tener en cuenta que esto sólo significa una lista de argumentos
vacía en C++. En C significa «un número indeterminado de
argumentos» (lo que es un «agujero» en C ya
que deshabilita la comprobación de tipos en ese caso). En ambos, C
y C++, la declaración func(void);
significa
una lista de argumentos vacía. La palabra clave void
significa «nada» en este caso (también puede
significar «sin tipo» en el caso de los punteros,
como se verá mas adelante en este capítulo).
La otra opción para las listas de argumentos se produce cuando no se sabe cuantos argumentos o qué tipos tendrán los argumentos; esto se conoce como lista de argumentos variable. Esta «lista incierta de argumentos» se representada con puntos suspensivos (...). Definir una función con una lista de argumentos variable es significativamente más complicado que definir una función normal. Se puede utilizar una lista de argumentos variable para una función que tiene un grupo de argumentos fijos si (por alguna razón) se quiere deshabilitar la comprobación del prototipo de función. Por eso, se debe restringir el uso de listas de argumentos variables en C y evitarlas en C++ (en el cual, como aprenderá, hay alternativas mucho mejores). El manejo de listas de argumentos variables se describe en la sección de librerías de la documentación de su entorno C particular.
Un prototipo de función C++ debe especificar el tipo de valor
devuelto de la función (en C, si no se especifica será por
defecto un int
). La especificación del tipo de
retorno precede al nombre de la función. Para especificar que no
se devolverá valor alguno, se utiliza la palabra reservada
void
. Esto provocará un error si se intenta devolver
un valor desde la función. A continuación hay algunos prototipos
completos de funciones:
int f1(void); // Devuelve un entero, no tiene argumentos int f2(); // igual que f1() en C++ pero no en C Stantard float f3(float, int, char, double); // Devuelve un float void f4(void); // No toma argumentos, no devuelve nada
Para devolver un valor desde una función, se utiliza la
sentencia return
. Esta sentencia termina la función y
salta hasta la sentencia que se halla justo después de la
llamada a la función. Si return
tiene un argumento, se
convierte en el valor de retorno de la función. Si una función
indica que retornara un tipo en particular, entonces cada
sentencia return
debe retornar un valor de ese
tipo. Puede haber más de una sentencia return
en una
definición de función:
//: C03:Return.cpp // Use of "return" #include <iostream> using namespace std; char cfunc(int i) { if(i == 0) return 'a'; if(i == 1) return 'g'; if(i == 5) return 'z'; return 'c'; } int main() { cout << "type an integer: "; int val; cin >> val; cout << cfunc(val) << endl; } ///:~
Listado 3.1. C03/Return.cpp
En cfunc()
, el primer if
que
comprueba que la condición sea true
sale de la función
con la sentencia return
. Fíjese que la declaración de
la función no es necesaria puesto que la definición aparece
antes de ser utilizada en main()
, de modo
que el compilador sabe de su existencia desde dicha definición.
Todas las funciones en la librería local de funciones de C están disponibles cuando se programa en C++. Se debería buscar bien en la librería de funciones antes de definir una propia - hay muchas probabilidades de que alguien haya resuelto el problema antes, y probablemente haya dedicado más tiempo pensando y depurando.
Una advertencia, del mismo modo: muchos compiladores incluyen muchas funciones extra que hacen la vida mucho mas fácil y resultan tentadoras, pero no son parte de la Librería C Estándar. Si está seguro de que jamás deseará portar la aplicación a otra plataforma (¿y quién está seguro de eso?), adelante -utilice esas funciones y haga su vida más fácil. Si desea que la aplicación pueda ser portada, debería ceñirse únicamente al uso de funciones de la Librería Estándar. Si debe realizar actividades específicas de la plataforma, debería intentar aislar este código de tal modo que pueda cambiarse fácilmente al migrarlo a otra plataforma. En C++, las actividades de una plataforma específica a menudo se encapsulan en una clase, que es la solución ideal.
La fórmula para usar una librería de funciones es la siguiente:
primero, encontrar la función en la referencia de programación
(muchas referencias de programación ordenan las funciones por
categoría además de alfabéticamente). La descripción de la
función debería incluir una sección que demuestre la sintaxis
del código. La parte superior de esta sección tiene al menos una
línea #include
, mostrando el fichero principal que
contiene el prototipo de función. Debe copiar este
#include
en su fichero para que la función esté
correctamente declarada. Ahora puede llamar la función de la
misma manera que aparece en la sección de sintaxis. Si comete un
error, el compilador lo descubrirá comparando la llamada a la
función con el prototipo de la cabecera e informará de dicho
error. El enlazador busca en la Librería Estándar por defecto,
de modo que lo único que hay que hacer es: incluir el fichero de
cabecera y llamar a la función.
Puede reunir funciones propias juntas en una librería. La
mayoría de paquetes de programación vienen con un
FIXME:bibliotecario que maneja grupos de módulos objeto. Cada
FIXME:bibliotecario tiene sus propios comandos, pero la idea
general es la siguiente: si se desea crear una librería, se debe
hacer un fichero cabecera que contenga prototipos de todas las
funciones de la librería. Hay que ubicar este fichero de
cabecera en alguna parte de la ruta de búsqueda del
preprocesador, ya sea en el directorio local (de modo que se
podrá encontrar mediante #include "header"
) o bien
en el directorio include
(por lo que se
podrá encontrar mediante #include
<header>
). Luego se han de juntar todos los módulos
objeto y pasarlos al FIXME:bibliotecario junto con un nombre
para la librería recién construida (la mayoría de los
bibliotecarios requieren una extensión común, como por ejemplo
.lib
o .a
). Se ha de
ubicar la librería completa donde residan todas las demás, de
manera que el enlazador sabrá buscar esas funciones en dicha
librería al ser invocadas. Pueden encontrar todos los detalles
en su documentación particular, ya que pueden variar de un
sistema a otro.