Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


5 Librería Estándar

§1  Sinopsis

C++ no llega al nivel de simplicidad de su antecesor C, pero al igual que aquél, tampoco dispone de utilidades o funciones para entrada o salida implementadas en el propio lenguaje, de modo que estas y otras muchas, como manejo de cadenas de caracteres (strings), manejo de ficheros, funciones matemáticas Etc. son implementadas en forma de librerías externas.

Una librería es un conjunto de recursos (algoritmos) prefabricados, que pueden ser utilizados por el programador para realizar determinadas operaciones ( 1.4.4a). Las declaraciones de las funciones (prototipos 4.4.1) utilizadas en estas librerías, junto con algunas macros y constantes predefinidas que facilitan su utilización, se agrupan en ficheros de nombres conocidos que suelen encontrarse en sitios predefinidos. Por ejemplo, en los sistemas UNIX, en  /usr/include. Estos ficheros se suelen llamar "de cabecera", porque es tradición utilizar las primeras líneas del programa para poner las directivas #include ( 4.9.10g) que los incluirá en el fuente durante la fase de preprocesado ( 1.4).

§2  Clases de librerías

Los compiladores C++ incluyen un amplio repertorio de clases, funciones y macros que permiten realizar una amplia variedad de tareas, incluyendo entradas/salidas de bajo y alto nivel; manipulación de cadenas alfanuméricas y ficheros; control de procesos (incluyendo multiproceso); manejo de memoria; cálculos matemáticos y un largo etcétera.

Este repertorio de recursos es denominado colectivamente como "Rutinas de librería"; "Librerías de ejecución" RTL ("Runtime Librarys") o simplemente "Librerías".   Puede decirse que el lenguaje aislado (tal cual) no tiene prácticamente ninguna utilidad sin la concurrencia de estas utilidades. El Estándar C++ las clasifica según su utilidad:

  • Soporte del lenguaje [7]

  • Diagnóstico

  • Utilidades generales

  • Cadenas alfanuméricas ("Strings")

  • Localización

  • Contenedores

  • Iteradores

  • Algoritmos

  • Cálculo numérico

  • Entrada/Salida

§2.1  Librería Estándar C++

Para poner un poco de orden, el Estándar C++ define la denominada Librería Estándar [3] que debe acompañar a cada implementación del compilador que se adhiera al estándar. Es decir: la norma determina cuales son, como se llaman y como se utiliza este conjunto de algoritmos que deben acompañar (como mínimo) a cada implementación del compilador que quiera llamarse "Estándar". La última versión, ISO/IEC 14882 del año 1998, especifica que se compone de 32 ficheros de cabecera de nombres fijos y conocidos agrupados según la funcionalidad de los algoritmos. Son los siguientes:

Ficheros Funcionalidad/funciones
<algorithm> Parte de la STL que describe los algoritmos ( 5.1.3)
<bitset> Parte de la STL relativa a contenedores tipo bitset ( 5.1.1e1). Set de valores booleanos.
<complex> Parte de la librería numérica de la STL relativa a los complejos ( ).
<deque> Parte de la STL relativa a contenedores tipo deque;  un tipo de colas: "Double-ended-queue"  ( 5.1.1c3).
<exception> Parte de la librería de diagnóstico relativa al manejo de excepciones ( 1.6)
<fstream> Flujos hacia/desde ficheros.
<functional> Parte de la STL relativa a Objetos-función ( 4.9.18f)
<iomanip> Manipuladores.
<ios> Supreclases para manejo de flujos de E/S.
<iosfwd> Contiene declaraciones adelantadas de todas las plantillas de flujos y sus typedefs estándar. Por ejemplo ostream.
<iostream> Parte del a STL que contiene los algoritmos estándar de E/S.
<istream> Algoritmos estándar de flujos de entrada.
<iterator> Parte de la STL relacionada con iteradores ( 5.1.2), un tipo de puntero que permite utilizar los algoritmos de la Librería con las estructuras de datos representadas por los contenedores.
<limits> Descripción de propiedades dependientes de la implementación ( 1.2.1) que afectan a los tipos fundamentales.
<list> Parte de la STL relativa a contenedores tipo list ( 5.1.1c4);  listas doblemente enlazadas ( 1.8)
<locale> Parte de la STL relativa a la internacionalización ( 5.2).
<map> Parte de la STL relativa a contenedores tipo map ( 5.1.1e4).
<memory> Utilidades relativas a la gestión de memoria, incluyendo asignadores ( 5.1.5) y punteros inteligentes (auto_ptr).
<new> Manejo de memoria dinámica ( 1.3.2)
<numeric> Parte de la librería numérica de la STL relativa a operaciones numéricas ( ).
<ostream> Algoritmos estándar para los flujos de salida.
<queue> Parte de la STL relativa a contenedores tipo queue ( 5.1.1d1);  colas de objetos ( 1.8).
<set> Parte de la STL relativa a contenedores tipo set ( 5.1.1e2).
<sstream> Flujos hacia/desde cadenas alfanuméricas.
<stack> Parte de la STL relativa a contenedores tipo stack ( 5.1.1c5);  pilas de objetos ( 1.8).
<stdexcept> Parte de la STL relativa a las clases de las que derivan los objetos lanzados por las excepciones ocasionadas en los algoritmos de la propia STL y otras expresiones.  Estas clases son utilizadas para reportar errores detectados durante runtime.  Los usuarios también pueden utilizar excepciones para reportar errores en sus propios programas ( 1.6.1a).
<streambuf> Parte de la STL relativa al Almacenamiento de flujos de E/S ("Stream buffers").  Define los tipos que controlan la capa de transporte ( 5.3.2f).
<string> Parte de la STL relativa a contenedores tipo string ( );  una generalización de las cadenas alfanuméricas para albergar cadenas de objetos.
<typeinfo> Mecanismo de identificación de tipos en tiempo de ejecución ( 4.9.14).
<utility> Parte de la STL que contiene elementos auxiliares como operadores y pares (pairs).
<valarray> Parte de la librería numérica de la STL relativa a manejo de matrices numéricas ()
<vector> Parte de la STL relativa a los contenedores tipo vector; una generalización de las matrices unidimensionales C/C++ ( 5.1.1c1)


Es digno de mención que aunque generalmente las librerías no aportan ninguna característica al lenguaje (se supone que son utilidades auxiliares que no forman parte del lenguaje propiamente dicho), una pequeña porción de la Librería Estándar C++ sí aporta características que se consideran pertenecientes a este, de forma que deben estar presentes los ficheros de cabecera correspondientes si se desea usarlas. En concreto se refieren a los siguientes elementos:

  • Operadores new, new[], delete y delete[] ( 4.9.20).

  • Clase type_info que corresponde al mecanismo RTTI de identificación de tipos en tiempo de ejecución representado por el operador typeid ( 4.9.14).

  • Rutinas de inicio y terminación ( 1.5).

  • Las excepciones estándar lanzadas por los algoritmos anteriores ( 1.6.1a)

§2.2  Librería C

Además de otras nuevas, cuyo diseño e importancia cambian drásticamente la filosofía del lenguaje, C++ incluye prácticamente la totalidad de funciones de la primitiva librería estándar C. A esta librería, mantenida por compatibilidad, la denominamos Librería clásica ( 5.5).

§2.3 Otras librerías

Además de las que proporciona el Estándar y las complementarias que pueda incluir por su cuenta el fabricante del compilador, en los proyectos reales se suelen utilizar infinidad de otras librerías, comerciales o públicas, que facilitan el trabajo en áreas particulares, y en este sentido, recordar que uno de los pecados que el programador novel debe evitar, es la tendencia a querer reinventar constantemente la rueda. De forma que el consejo es utilizar en lo posible librerías que estén probadas y sean fácilmente accesibles. Aunque hay que tener cuidado, porque determinados proyectos exigen que las librerías utilizadas estén perfectamente documentadas y en ciertos casos, que se cuente con los fuentes correspondientes.

Aunque el mundo del software se mueve muy rápidamente y cualquier indicación al respecto puede quedar rápidamente anticuada, en la página Librerías C/C++ de este manual puede encontrar una selección de ellas. Aquí nos limitaremos a hacer especial mención a las Librerías Boost, por su condición de libres y su alta calidad técnica. Algunas de ellas han sido adoptadas por la última versión del Estándar (C++11) [8]. 

§3 Componentes

Al referirnos a las librerías C++ utilizamos la terminología siguiente:

  • RTL  Conjunto de librerías que acompañan a un compilador ("Runtime Library"), sean o no estándar.

  • Librería Estándar. Conjunto de librerías cuyo contenido está definido por el Estándar C++ (abreviadamente LE)

  • Libería clásica. Parte de la Librería Estándar correspondiente al C clásico.

  • STL ("Standard Templete Library"). Parte de la LE genuina de C++ (que no es heredada de C) y que responde a la forma "++" de hacer las cosas.

La calidad de un compilador C++ viene determinada en gran medida por la calidad y cantidad de su RTL; por su grado de adherencia al Estándar [6] y por el grado de soporte que proporciona para la plataforma concreta a que se destina.

Nota: en lo que concierne a la programación para MS Windows (las referencias a la plataforma Wintel surgen de forma constante e inevitable), los compiladores Borland C++ y MS Visual C++ son quizás los más conocidos y utilizados.  El primero incluye una completa librería denominada VCL (Visual Component Library 4.11.8b). La del segundo es conocida como MFC (Microsoft Foundation Classes 1.7.2).

Linux dispone de su propio compilador: GNU Cpp, que comenzó siendo un compilador C y ha evolucionado a un producto que es en realidad un superconjunto de compiladores. No solo puede compilar código C y C++;  también otros lenguajes como Ada o Fortran;  de forma que es común referirse a ellos como GCC ("GNU Compiler Collection").  La buena noticia es que existen versiones de este compilador para plataformas Windows, de forma que incluso en este ambiente "Propietario" pueden realizarse desarrollos utilizando herramientas "Open source" (ver recuadro en 1.4.0a1).


A grandes rasgos la librería Estándar C++ comprende los siguientes módulos:

  • La denominada Librería Estándar de Plantillas ( 5.1), abreviadamente STL ("Standard Templete Library").

    • Librería numérica. Parte de la STL que contiene algoritmos especialmente concebidos para computación numérica. Por ejemplo, una clase valarray optimizada para matrices numéricas y una clase complex para manejo y representación estandarizada de números complejos (en realidad una plantilla con especializaciones para los tipos float, double y long double).

    • Utilidades. Parte de la STL dedicada a elementos auxiliares tales como adaptadores y la clase auto_ptr.

  • Unas utilidades de entrada/salida de flujos, denominadas genéricamente iostreams ( 5.3).

  • Una utilidad locale para manejo de localismos [4] ( 5.2).

  • Una clase string para manejo estandarizado de cadenas de caracteres.  En realidad es una instanciación (especialización) de la plantilla basic_string para cadenas de caracteres. 

  • Un esquema para describir de modo uniforme el entorno de ejecución mediante la utilización de una clase estándar denominada numeric_limits y especialización para cada uno de los tipos de datos fundamentales.

  • Utilidades para manejo de memoria ( 5.1.5).

  • Soporte para utilización de juegos de caracteres y signos de diversos idiomas.

  • Utilidades de diagnóstico y manejo de errores. Incluyen manejo de excepciones ( 1.6) y la macro assert ( 1.4.5).

§4 Funcionalidad

Si atendemos a su funcionalidad,  las utilidades ofrecidas por la Librería Estándar pueden agruparse en:

  • Clasificación: Clasifican caracteres ASCII, como letras, caracteres de control (no imprimibles), Mayúsculas/minúsculas etc. Se definen en la cabecera <ctype.h>.

  • Entradas/Salidas de Consola: Estas son las denominadas entrada/salida estándar. Por defecto se refieren al teclado y a la pantalla (no pueden utilizarse directamente en aplicaciones de interfaz gráfica).

  • Conversión: Convierten caracteres y cadenas de caracteres desde formato alfabético a numérico de diversos tipos (float, int, long).  También realizan la conversión inversa: de formatos numéricos a representaciones alfabéticas y de mayúsculas a minúsculas y viceversa.

  • Diagnóstico: Son rutinas destinadas a comprobaciones; a descubrir y corregir posibles errores.

  • Directorio: Rutinas para manejo de directorios y sus direcciones (path names).

  • En linea (Inline): Rutinas para versiones inline de funciones. El compilador genera el código correspondiente para las versiones inline cuando se utiliza #pragma intrinsic o si se solicita optimización al compilador (optimización de tiempo de ejecución).

  • Entrada/Salida. Son rutinas que proporcionan manejo de flujos y operaciones de Entrada/Salida a bajo nivel (de Sistema Operativo).

  • Manipulación. Manejo de cadenas y bloques de memoria: copiar, comparar, convertir y buscar.

  • Matemáticas: Para realizar cálculos matemáticos.

  • De Memoria: Proporcionan asignación de memoria dinámica.

  • Miscelánea. Se agrupan aquí rutinas varias, como las que posibilitan saltos (goto) no locales y las que manejan diferencias de tipo cultural o de lenguaje. Por ejemplo representación de números, de moneda, formatos de fecha y hora, clasificación de tipo alfabético, etc.

  • Control de proceso. Rutinas que permiten invocar y terminar nuevos procesos desde otra rutina.

  • Fecha y hora. Incluyen rutinas para conversión y manipulación de variables de medida del tiempo (fecha y hora).

  • Argumentos variables. Rutinas utilizadas cuando se usan listas variables de argumentos, como en los casos de printf(), vscanf(), etc.

§5  Utilización

La utilización de la Librería Estándar C++ requiere de dos tipos de condiciones que podríamos describir como formales y conceptuales.


§5.1
  En cuanto a las primeras (condiciones formales) y habida cuenta que las utilidades aparecen en forma de funciones, resulta evidente que su utilización exige incluirlas en nuestro programa. Para ello se necesitan tres pasos (en realidad las exigencias son las mismas que con cualquier otra función, la diferencia estriba en la forma en que se realizan los pasos b y c):

  a:  Incluir en el código fuente las invocaciones a las funciones que estamos utilizando. Ejemplo:

printf("Esto es una llamada a la función \"printf\" de librería\n");

  b:  Incluir en el código fuente los prototipos de dichas funciones.

Puesto que los prototipos ya están incluidos en los ficheros estándar de cabecera, hay que indicar al compilador que los incluya [2]. Esto se realiza poniendo en nuestro fuente (normalmente al principio) una directiva de preprocesado #include ( 4.9.10g) que señala el fichero de cabecera que se debe añadir. Por ejemplo, si el manual indica que la función printf está definida en el fichero de cabecera stdio.h ponemos en nuestro código:

#include <stdio.h>

  c:  Incluir en el fuente las definiciones de las funciones utilizadas. Como alternativa se puede indicar al compilador que tales definiciones están en ficheros compilados previamente.

En este último caso se dice que las definiciones de las funciones están en librerías ( 1.4) de las que existen dos tipos:estáticas (.LIB, .OBJ y .BPI) y dinámicas (.DLL). Toda la información que necesita el compilador está contenida en los ficheros de cabecera, por lo que las operaciones correspondientes son realizadas de forma automática; con la sola condición de que los ficheros y librerías correspondientes sean accesibles al enlazador.


  No olvidar que la entidades de la Librería Estándar C++ (que no están en ficheros de cabecera .h), se han definido en un espacio de nombres denominado std, por lo que es preciso referirse a él específicamente cuando se quieran utilizar estos recursos ( 4.1.11c2).


§5.2  Respecto a las que hemos denominado "condiciones conceptuales", damos por sentado que la utilización de algoritmos de la Librería exige conocerlos. Pero debemos advertir que existe una gran diferencia entre las rutinas de la que hemos denominado librería clásica y los recursos de la nueva Librería Estándar de Plantillas STL.

§5.2a  En general los algoritmos contenidos en la librería clásica realizan tareas muy sencillas bajo la forma de funciones que, con un cierto número de argumentos, realizan la tarea encomendada. A veces devolviendo un objeto (que puede ser una estructura). Su utilización exige poco más que repasar esta colección de funciones (209 en la última versión del Estándar) hasta encontrar la que mejor se ajusta a nuestras necesidades. Para esto lo mejor es recurrir a la clasificación temática ofrecida en la documentación que acompaña a los compiladores antes que a la lista alfabética de las mismas. A continuación estudiamos atentamente su prototipo y la explicación de funcionamiento que acompaña el manual (eventualmente incluso con un ejemplo) y ya estamos listos para utilizarla. En el peor de los casos suelen bastar un par de pruebas hasta que conseguimos ajustar los argumentos de forma que se consiga el resultado esperado.

Nota: a título de ejemplo, el fichero BCB5RTL.HLP, que contiene la "C Runtime Library reference" (nombre con que aparece la Librería Clásica en la documentación de Borland), contiene dos secciones; la primera, denominada "Categorical Routines and Types Listing", contiene la clasificación temática; la segunda, denominada "Alphabetical Routines and Types Listing", contiene la relación alfabética [5].


Como última recomendación al respecto, sobre todo si pensamos que nuestro programa tendrá una larga vida y/o pensamos portarlo a otras plataformas, aconsejaría echar un vistazo a la sección de "compatibilidad" que acompaña a la descripción de cada función en la documentación de la mayoría de compiladores. Suele ocurrir que el compilador ofrezca muchas más funciones que las estrictamente Estándar C++, y que estas no estén claramente separadas del resto, de forma que es posible que elijamos una función que resuelve nuestro problema pero no es portable, en cuyo caso aconsejaría evitarlas dentro de lo posible.

Nota: como ejemplo escogido al azar,  la función max(), que aparece en primer lugar en la clasificación temática de la documentación Borland C (Runtime Library reference: "Categorical Routines and Types Listing"), es una función ofrecida por este compilador para el entorno Windows-32 pero no es portable a UNIX ni tampoco pertenece a los estándares C o C++.


§5.2b  Para todo el que no se haya asomado antes a la programación genérica, la utilización de la nueva Librería de Plantillas (STL) exige la asimilación de un nuevo paradigma;  una nueva forma de pensar que, si se tiene experiencia en programación tradicional, exige incluso un "cambio de chip", ya que estas herramientas conforman un mundo enteramente nuevo y singular.

Con independencia de las explicaciones más detalladas que incluimos en el siguiente capítulo ( 5.1), adelantemos aquí que la STL ofrece un conjunto de recursos flexible, potente y altamente optimizado para resolver una gran variedad de situaciones de programación. Pero esta versatilidad y potencia tributan un precio: su alto grado de parametrización supone algo más que utilizar herramientas aisladas. Es necesario saber ensamblar entre sí los recursos disponibles, que no trabajan aisladamente y que son básicamente de tres tipos: contenedores, iteradores y algoritmos. El resultado es que el proceso de utilizar la STL es algo más complicado que la simple búsqueda de una función que resuelva nuestras necesidades;  que la curva de aprendizaje es ciertamente más ardua que en el caso de la librería clásica, y  que los conceptos involucrados son más abstractos y requieren un conocimiento más extenso de sus fundamentos para utilizarlos. Desde luego, la descripción detallada de todos sus elementos exige un libro completo, y de hecho son muchos los que se han publicado con el objeto exclusivo de describir la STL y/o sus técnicas de uso. En compensación, las contrapartidas obtenidas justifican sin duda el esfuerzo. El programador puede concentrarse más en el problema que en los detalles de su implementación en el fuente, se incremente la productividad y se gana en claridad conceptual.

§6  Funciones y macros

Hay que señalar que algunas funciones pueden venir implementadas de dos formas: como macro y como función. Por ejemplo, la función isalnum, contenida en <ctype.h>.  Esto significa que además de estar implementada como una función en la correspondiente librería (que se utiliza con los argumentos señalados en el manual), está definida como una macro de preprocesado en <ctype.h>, de forma que salvo indicación contraria al compilador, el preprocesador transforma la llamada del programador en otra serie de sentencias equivalentes que utilizan los mismos argumentos. El resultado es que en el código resultante, que luego pasa al compilador y al enlazador, no existe nada parecido a una llamada a una función isalnum (técnicamente el proceso es una sustitución inline de la función de librería, 4.4.6b).

Para evitar que esto ocurra y por consiguiente que la llamada a islanum nunca llegue a producirse, basta con indefinir la correspondiente directiva. Esto se hace incluyendo al principio del código, y debajo de la línea #include <ctype.h>, una línea de preprocesado adecuada, en este caso:  #undef isalnum, con lo que el #define del preprocesador quedará sin efecto, con el resultado de que todas las invocaciones a isalnum de nuestro código serán respetadas por el preprocesador. Más tarde el enlazador cargará el código de isalnum (que extrae de la librería) con el resto de nuestro programa, y colocará en cada punto de nuestro código donde aparezca una invocación a isalnum, un salto a la dirección adecuada.

En estos casos, el resultado es el mismo en ambas modalidades: como macro (por defecto) o como función de librería. La elección de una u otra es cuestión de optimización. Para tomar una decisión es necesario comprobar y valorar dos aspectos:

  • El tamaño del ejecutable que resulta en uno y otro caso. Posiblemente mayor si se utiliza la función muchas veces y se adopta la macro.

  • La velocidad de ejecución. Posiblemente mayor si se utiliza la función muchas veces (en bucles muy largos) y se utiliza la macro [1].

  Inicio.


[1]  El cronometraje se puede realizar sencillamente a mano, con un cronómetro, o colocando marcadores en sitios estratégicos del programa que, utilizando el reloj del sistema, muestren los tiempos consumidos. Ver ejemplo ( 9.1).

[2]  Estrictamente hablando estas operaciones son realizadas por el preprocesador, el analizador sintáctico, el compilador o el enlazador según los casos. Nos referimos a todos estos módulos como "compilador" en un sentido genérico, pues desde un punto de vista práctico, en algunos casos (como el copilador C++Builder de Borland), solo hay que realizar una llamada al "compilador" y este se encarga de la totalidad del proceso ( 1.4.0).

[3]  En lo sucesivo la designaremos así, con mayúsculas iniciales, para simbolizar que es una librería única y perfectamente especificada.

[4]  "Locales" son conjuntos de datos que indican preferencias culturales. Por ejemplo: formas de representar la hora, las fechas, el "punto" decimal, etc. La utilidad locale de la Librería Estándar es la forma que tiene C++ de dar soporte a la internacionalización de los programas.

[5]  Aconsejo descargar la documentación C++ del "Site" de Borland aunque se trabaje con otro compilador. Además de gratuita, es muy completa, bien estructurada y con abundantes ejemplos.

[6]  Posiblemente esta afirmación puede resultarle sorprendente a más de uno.  En informática y en muchos aspectos de la vida, las leyes y normas suelen ir muy por detrás de mundo y la vida cotidianos. Es frecuente que las estandarizaciones y normativas se limiten a consagrar "oficialmente" hábitos y costumbres largamente consagrados ya por la práctica. Sin embargo, en el caso del C++ los acontecimientos se han sucedido a veces de forma contraria. En el Comité de Estandarización participan representantes de la industria (del software) junto con miembros de laboratorios de investigación y desarrollo. Teóricos de los lenguajes; estudiosos de las teorías de computación, de compiladores, Etc. que han tenido tanto o más peso que aquellos. El resultado es que en algunos casos el Estándar a introducido y aprobado herramientas que no estaban suficientemente probadas o generalizadas en la práctica. Construcciones teóricas que ni siguiera eran soportadas por la mayoría de compiladores disponibles comercialmente. Esto, que incluso ha originado comentarios negativos por parte de algunos miembros del Comité, ha motivado que incluso algún tiempo después de publicado el Estándar, muchos compiladores no hubiesen implementado aún todas sus características.

[7]  Este primer grupo constituye un caso especial: En principio una librería es algo que ayuda a realizar determinadas operaciones con más facilidad que la ofrecida por los métodos implementados en el lenguaje, o que permite hacer algo que no puede hacerse con las herramientas definidas en él, pero que en rigor no forma parte del lenguaje en sí mismo. Sin embargo C++ ha sido dotado de ciertas extensiones que se han incluido en forma de librerías (denominadas "de soporte" 1.5.2). De forma que en cierto sentido dichas librerías sí forman parte del lenguaje.

[8]  Si quiere una perspectiva de a la evolución de la Librería Estándar y del estado actual (2013) de la cuestión respecto su uso, versus librerías externas, puede interesarle el artículo (en Inglés) de Jeff Cogswell "Comparing the C++ Standard and Boost".