Diferencias entre C y C++. En este capítulo daremos el salto de C a C++, un lenguaje que extiende a su antecesor con características importantes como la posibilidad de programación orientada a objetos, plantillas, manejo avanzado de memoria y una biblioteca estándar mucho más rica. Analizaremos las ventajas clave que aporta C++ sobre C, revisaremos su sintaxis específica (por ejemplo, la inicialización de variables y las clases), y compararemos la nueva cabecera <iostream>
con las funciones clásicas de stdio.h
. También aprenderás a trabajar con la clase std::string
para gestionar cadenas, a utilizar espacios de nombres (namespace
) para organizar código y evitar colisiones, y a aplicar la sobrecarga de funciones, una técnica que permite reutilizar el mismo nombre de función con diferentes parámetros. Empezamos!!!!
10.1 Ventajas de C++ sobre C
Aunque C y C++ comparten muchas similitudes —de hecho, C++ fue originalmente una extensión de C llamada “C con clases”—, C++ introduce una serie de mejoras fundamentales que lo convierten en una opción más moderna, estructurada y potente para el desarrollo de software. A continuación, analizamos con detalle las principales ventajas de C++ sobre C:
1. Programación orientada a objetos (OOP)
C++ incorpora de forma nativa el paradigma orientado a objetos, que permite estructurar programas en torno a clases y objetos. Esto ofrece beneficios significativos como:
- Encapsulamiento: puedes agrupar datos y funciones dentro de una misma clase, limitando el acceso externo a ciertos miembros mediante modificadores como
private
,public
yprotected
. - Herencia: puedes crear nuevas clases a partir de otras existentes, reutilizando código y extendiendo funcionalidades.
- Polimorfismo: permite tratar objetos derivados como si fueran del tipo base.
Ejemplo: En C++ puedes definir una clase Persona
con atributos y métodos, algo que en C requeriría estructuras y punteros a funciones.
class Persona {
public:
string nombre;
int edad;
void saludar() {
cout << "Hola, me llamo " << nombre << " y tengo " << edad << " años." << endl;
}
};
2. Manejo avanzado de cadenas con std::string
A diferencia de C, que trata las cadenas como arreglos de caracteres (char[]
), C++ incluye la clase std::string
, mucho más segura y flexible:
- Permite concatenación directa con el operador
+
- Tiene métodos como
.length()
,.substr()
,.find()
, etc. - Se gestiona dinámicamente (no necesitas preocuparte por el tamaño del array o por el carácter
\0
al final)
Ejemplo:
string nombre = "Juan";
string saludo = "Hola " + nombre;
cout << saludo << endl; // Salida: Hola Juan
3. Biblioteca Estándar (STL)
C++ ofrece Standard Template Library (STL), esta librería incluye estructuras de datos listas para usar como:
vector
(arreglos dinámicos)map
(diccionarios)set
(conjuntos)list
,stack
,queue
, entre otras.
Además, C++ puedes usar plantillas (templates
), que hacen posible escribir funciones y clases genéricas.
4. Sobrecarga de funciones y operadores
C++ permite definir múltiples funciones con el mismo nombre, diferenciándolas por su lista de parámetros (sobrecarga). También puedes redefinir el comportamiento de operadores (+
, -
, ==
, etc.) para tipos definidos por el usuario.
Ejemplo: Sobrecargar +
para una clase Punto
de representación de coordenadas:
class Punto {
public:
int x, y;
Punto(int _x, int _y) : x(_x), y(_y) {}
Punto operator+(const Punto& otro) {
return Punto(x + otro.x, y + otro.y);
}
};
5. Mejor manejo de memoria y recursos
C++ mejora la gestión de memoria respecto a C con herramientas como:
- Constructores y destructores automáticos
- Operadores
new
ydelete
en lugar demalloc
yfree
- Tipos RAII (Resource Acquisition Is Initialization), lo que permite liberar recursos de forma automática al salir del ámbito.
6. Espacios de nombres (namespace)
En C++, puedes usar namespaces para evitar colisiones entre nombres de variables, funciones o clases.
Ejemplo:
namespace utilidades {
void mostrarMensaje() {
cout << "Desde el namespace utilidades" << endl;
}
}
int main() {
utilidades::mostrarMensaje();
return 0;
}
10.2 Sintaxis específica de C++
Aunque C++ mantiene casi toda la sintaxis base de C (sentencias, estructuras de control, funciones, etc.), introduce una serie de ampliaciones sintácticas que permiten trabajar de forma más modular.
Veamos las principales diferencias de sintaxis que distinguen a C++ de C, y cómo se utilizan en la práctica.
1. Declaración de variables en cualquier parte del código
En C, las variables deben declararse al principio de un bloque {}
. En C++, se pueden declarar en cualquier punto, lo que permite una mayor claridad y coherencia al escribir código.
- C (obligatorio al principio):
int main() {
int a = 10;
int b = 5;
if (a > b) {
int c = a - b; // En C esto puede causar error si se declara dentro de un bloque sin estar al inicio.
printf("%d\n", c);
}
return 0;
}
- C++ (permitido en cualquier lugar):
int main() {
int a = 10;
int b = 5;
if (a > b) {
int c = a - b; // Correcto y común en C++
cout << c << endl;
}
return 0;
}
2. Uso del tipo bool
y constantes true
y false
En C, no existe un tipo de dato bool
(se suele usar int
y tratar 0 como falso). C++ incorpora directamente el tipo bool
y las constantes true
y false
.
Ejemplo:
bool esMayor = (10 > 5); // true
if (esMayor) {
cout << "Es verdadero." << endl;
}
3. Entrada y salida estándar con cin
y cout
En lugar de usar printf()
y scanf()
, C++ utiliza el sistema de entrada/salida basado en flujos:
cout
para salida estándarcin
para entrada estándar
- Ejemplo:
#include <iostream>
using namespace std;
int main() {
int edad;
cout << "Introduce tu edad: ";
cin >> edad;
cout << "Tienes " << edad << " años." << endl;
return 0;
}
Este sistema permite formatear la salida de forma más flexible, y encadena múltiples elementos con el operador <<
.
4. Constructores y destructores
Las clases en C++ pueden tener funciones especiales que se ejecutan automáticamente al crear o destruir un objeto:
- Constructor: se llama al crear el objeto.
- Destructor: se llama al destruir el objeto (por ejemplo, cuando termina su ámbito de vida).
- Ejemplo:
class Persona {
public:
string nombre;
Persona(string n) {
nombre = n;
cout << "Persona creada: " << nombre << endl;
}
~Persona() {
cout << "Persona destruida: " << nombre << endl;
}
};
5. Plantillas (templates)
Las plantillas permiten definir funciones y clases genéricas, que se adaptan automáticamente al tipo de dato que se les pase.
- Ejemplo de función plantilla:
template <typename T>
T sumar(T a, T b) {
return a + b;
}
int main() {
cout << sumar(3, 4) << endl; // int
cout << sumar(3.2, 1.8) << endl; // double
}
6. Uso de referencias (&
)
C++ permite pasar variables por referencia a funciones, evitando el uso explícito de punteros. Esto es más intuitivo y seguro en muchos contextos.
- Ejemplo:
void duplicar(int& valor) {
valor *= 2;
}
int main() {
int x = 5;
duplicar(x);
cout << x << endl; // Salida: 10
}
7. Sobrecarga de funciones
Puedes definir varias funciones con el mismo nombre pero distinta cantidad o tipo de parámetros. Esto permite escribir interfaces más limpias y expresivas.
- Ejemplo:
int sumar(int a, int b) {
return a + b;
}
double sumar(double a, double b) {
return a + b;
}
10.3 iostream
frente a stdio.h
Una de las diferencias fundamentales entre C y C++ radica en el sistema de entrada/salida. Mientras que C utiliza la biblioteca stdio.h
(standard input/output), C++ introduce una alternativa más moderna y segura: iostream
.
Veamos las diferencias entre ambos.
1. ¿Qué es stdio.h
?
En C, la entrada y salida se realizan mediante funciones como:
printf()
→ Imprime en pantallascanf()
→ Lee datos desde tecladofprintf()
/fscanf()
→ I/O en archivosputchar()
/getchar()
→ Entrada/salida de caracteres
- Ejemplo en C (
stdio.h
):
r#include <stdio.h>
int main() {
int edad;
printf("Introduce tu edad: ");
scanf("%d", &edad);
printf("Tienes %d años.\n", edad);
return 0;
}
2. ¿Qué es iostream
?
C++ ofrece iostream
(Input/Output Stream), una biblioteca basada en el concepto de flujos de datos (streams). Sus principales elementos son:
cout
→ Imprime en pantalla (console output)cin
→ Lee desde teclado (console input)cerr
→ Salida estándar de erroresclog
→ Salida estándar para logs (sin bloquear flujo)
- Ejemplo en C++ (
iostream
):
#include <iostream>
using namespace std;
int main() {
int edad;
cout << "Introduce tu edad: ";
cin >> edad;
cout << "Tienes " << edad << " años." << endl;
return 0;
}
3. Diferencias clave entre stdio.h
y iostream
Característica | stdio.h | iostream |
---|---|---|
Estilo | Procedural (C) | Orientado a objetos (C++) |
Sintaxis | Verbosa y propensa a errores | Limpia y legible |
Tipo de datos | No detecta errores de tipo | Usa tipos fuertemente tipados |
Internacionalización | Requiere formateo manual | Compatible con locale |
Extensibilidad | Limitada | Altamente extensible |
Sobrecarga de operadores | No soporta | Sí (<< y >> para I/O) |
Entrada/salida personalizada | Difícil con estructuras propias | Fácil con operadores sobrecargados |
Veamos algunos ejemoplos comparativos.
// C
int x = 10;
printf("Valor: %d\n", x);
// C++
int x = 10;
cout << "Valor: " << x << endl;
Y con múltiples valores:
// C
int a, b;
scanf("%d %d", &a, &b);
// C++
int a, b;
cin >> a >> b;
Ojo, Errores comunes que se suele cometer en scanf()
- Olvidar el
&
enscanf()
- Usar el formato incorrecto (ej.
%f
en lugar de%lf
)
@Tip. cin
: no necesita símbolos como &
, lo que hace que sea menos propenso a errores.
5. ¿Cuándo usar cada uno?
Caso | Recomendación |
---|---|
Programas en C | stdio.h |
Programas modernos en C++ | iostream |
Entrada/salida rápida para competiciones | stdio.h (más veloz sin sincronización) |
Personalización y extensibilidad | iostream (clases y objetos) |
10.4 Manejo de cadenas con string
en C++
En C, las cadenas se manejan como arrays de caracteres terminados en '\0'
, lo que obliga a utilizar funciones de la biblioteca <string.h>
para su manipulación. En C++, aunque se pueden seguir usando las cadenas estilo C, se recomienda usar la clase std::string
del encabezado <string>
, que proporciona una forma más segura, flexible y sencilla de trabajar con texto.
1. ¿Qué es std::string
?
std::string
es una clase de la biblioteca estándar de C++ que permite trabajar con cadenas de texto. Al ser una clase, incluye métodos y operadores sobrecargados que facilitan tareas comunes como concatenar, comparar, buscar o extraer subcadenas.
Incluye automáticamente:
- Gestión dinámica de memoria.
- Comparación de cadenas con
==
,<
, etc. - Concatenación con
+
o+=
. - Acceso por índice con
[]
.
2. Incluir string
y sintaxis básica
Como cualquier librería, para utilizarla debemos incluirla con la sentencia #include al principio del programa
#include <iostream>
#include <string>
using namespace std;
int main() {
string nombre = "Juan";
cout << "Hola " << nombre << endl;
return 0;
}
3. Operaciones comunes con string
Al ser String una clase, nos ofrece un conjunto de métodos a los que podemos invocar y que facilitan la manipulación de las cadenas de caracteres. Veamos los más importantes.
- Concatenación. Une dos cadenas de caracteres.
string saludo = "Hola";
string nombre = "Ana";
string mensaje = saludo + " " + nombre;
cout << mensaje << endl; // Hola Ana
- Longitud. Devuelve el tamaño de la cadena de caracteres
string texto = "Programación";
cout << "Longitud: " << texto.length() << endl;
- Acceso a caracteres. Devuelve el carácter de la cadena de la posición N, indicado entre []
string palabra = "C++";
cout << palabra[0]; // 'C'
palabra[1] = '#'; // ahora la cadena es "C#+"
- Comparación Compara dos cadenas de caracteres.
string a = "Hola", b = "Adiós";
if (a > b) cout << "a es mayor alfabéticamente" << endl;
- Subcadenas. Extrae una cadena de caracteres de otra, indicando la posición inicial y el número de caracteres que se quiere extraer.
string frase = "Lenguaje C++";
string parte = frase.substr(9, 3); // "C++"
- Buscar dentro de una cadena. Busca una cadena de caracteres dentro de otra y devuelve la posición si existe.
string frase = "Hola mundo";
size_t pos = frase.find("mundo");
if (pos != string::npos)
cout << "Encontrado en posición: " << pos << endl;
- Eliminar parte de una cadena. Elimina una cadena de caracteres de otra.
string texto = "Hola mundo cruel";
texto.erase(10, 6); // Borra "cruel"
cout << texto; // Hola mundo
4. Conversión entre string
y tipos primitivos
En C++ podemos realizar conversiones de una variable entre distintos tipos. En ocasiones es necesario poder realizar estas conversiones, veamos unos ejemplos:
- De
string
aint
/float
#include <string>
#include <iostream>
using namespace std;
int main() {
string numero = "123";
int valor = stoi(numero);
float real = stof("3.14");
cout << valor << " y " << real << endl;
return 0;
}
- De
int
/float
astring
int edad = 30;
string texto = to_string(edad);
cout << "Tienes " + texto + " años." << endl;
5. Comparación con cadenas estilo C
Veamos con una tabla resumen las diferencias entre C y C++ para el manejor de cadenas de texto.
Característica | char[] (C) | string (C++) |
---|---|---|
Necesita \0 | Sí | No |
Memoria manual | Sí | No |
Concatenación | strcat() | + o += |
Comparación | strcmp() | == , < , etc. |
Subcadenas | Manual (strncpy ) | substr() |
Lectura con espacios | Difícil (gets , fgets ) | Fácil con getline() |
6. Leer líneas completas
Otra de las diferencias que nos ofrece C++ con respecto a C es la poisibilidad de leer cadenas de texto completas hasta que encuentra un ‘\n’.
string linea;
cout << "Introduce una frase: ";
getline(cin, linea); // permite leer espacios
cout << "Has dicho: " << linea << endl;
10.5 Espacios de nombres (namespace) en C++
En programas grandes, es común que diferentes partes del código usen los mismos nombres para variables, funciones o clases. Para evitar conflictos de nombres, C++ introduce el concepto de espacios de nombres o namespaces.
¿Qué es un namespace?
Un namespace es un contenedor que encapsula identificadores (como funciones, variables o clases) bajo un nombre específico. De esta forma, se pueden organizar mejor los elementos del código y evitar que haya conflictos con nombres repetidos.
Sintaxis básica
namespace MiEspacio {
int valor = 42;
void saludar() {
std::cout << "Hola desde MiEspacio" << std::endl;
}
}
Para acceder a lo que hay dentro del namespace:
MiEspacio::saludar(); // llama a la función
std::cout << MiEspacio::valor << std::endl;
Ventajas del uso de namespaces
- Permiten modularizar el código.
- Evitan conflictos con nombres repetidos.
- Son especialmente útiles cuando se usan librerías externas.
std
— El espacio de nombres estándar
En C++, muchas funciones y clases de la biblioteca estándar (como cout
, cin
, string
) están dentro del namespace std
.
#include <iostream>
int main() {
std::cout << "Hola mundo" << std::endl;
return 0;
}
Alternativamente, se puede hacer uso de la sentencia using para indicar el espacion de nombres a utilizar:
using namespace std;
int main() {
cout << "Hola mundo" << endl;
return 0;
}
Importante: aunque
using namespace std;
ahorra escritura, en proyectos grandes se recomienda no usarlo en archivos de cabecera ni a nivel global, para evitar ambigüedades.
Alias de namespace
También es posible crear un alias para un namespace largo:
namespace ProyectoLargoNombre = MiEspacio;
ProyectoLargoNombre::saludar();
Namespaces anidados
Los espacios de nombres pueden estar dentro de otros:
namespace A {
namespace B {
void mensaje() {
std::cout << "Desde A::B" << std::endl;
}
}
}
A::B::mensaje(); // Acceso al namespace anidado
Veamos un ejemplo completo de como se utilizaría:
#include <iostream>
using namespace std;
namespace Matematicas {
double pi = 3.1416;
double cuadrado(double x) {
return x * x;
}
}
int main() {
cout << "PI = " << Matematicas::pi << endl;
cout << "Cuadrado de 5: " << Matematicas::cuadrado(5) << endl;
return 0;
}
10.6 Sobrecarga de funciones en C++
La sobrecarga de funciones (function overloading) es una característica de C++ que permite definir múltiples funciones con el mismo nombre, pero con parámetros diferentes. Esta es una de las formas en que C++ admite polimorfismo.
¿Qué es la sobrecarga de funciones?
Es cuando se declaran varias funciones con el mismo nombre pero con una diferente lista de parámetros (tipo, número o ambos). El compilador decide cuál función ejecutar dependiendo de los argumentos que se le pasen.
Ejemplo básico
#include <iostream>
using namespace std;
void saludar() {
cout << "Hola" << endl;
}
void saludar(string nombre) {
cout << "Hola, " << nombre << endl;
}
void saludar(string nombre, int edad) {
cout << "Hola, " << nombre << ". Tienes " << edad << " años." << endl;
}
int main() {
saludar();
saludar("María");
saludar("Carlos", 30);
return 0;
}
Salida:
Hola
Hola, María
Hola, Carlos. Tienes 30 años.
Reglas de la sobrecarga
Para que la sobrecarga sea válida, las funciones deben diferir en:
- Número de parámetros
- Tipo de parámetros
- Orden de parámetros
@Tip. No se puede sobrecargar una función solo cambiando el tipo de retorno.
// Esto no es válido solo por cambiar el tipo de retorno
int suma(int a, int b);
float suma(int a, int b); // Error
Veasmos algunos ejemplos con funciones matemáticas
int suma(int a, int b) {
return a + b;
}
float suma(float a, float b) {
return a + b;
}
double suma(double a, double b) {
return a + b;
}
Sobrecarga con parámetros por defecto
La sobrecarga también puede coexistir con valores por defecto, aunque hay que tener cuidado para no generar ambigüedades:
void imprimir(int a, int b = 0) {
cout << "a: " << a << ", b: " << b << endl;
}
void imprimir(string texto) {
cout << "Texto: " << texto << endl;
}
Beneficios de la sobrecarga
- Facilita la legibilidad del código.
- Permite construir interfaces más intuitivas.
- Reduce la necesidad de inventar nombres distintos para cada versión de una función.
Conclusión
En este capítulo hemos visto las principales diferencias entre C y C++. Aprendimos las ventajas de C++, su sintaxis más moderna, el uso de iostream
para entrada/salida, el manejo de cadenas con string
, la utilidad de los namespace
para evitar conflictos, y la sobrecarga de funciones como forma de polimorfismo. Vayamos a practicar con unos ejercicios propuestos.
Ejercicio 1 – Calculadora básica con sobrecarga de funciones
Enunciado
Crea un programa en C++ con una función llamada calcular
sobrecargada tres veces para sumar int
, sumar double
y concatenar dos std::string
. Desde main()
pide al usuario dos valores; detecta si son enteros, reales o palabras (sin espacios) y muestra el resultado usando la versión de calcular
adecuada.
Enunciado
Crea un programa en C++ con una función llamada
calcular
sobrecargada tres veces para sumar int
, sumar double
y concatenar dos std::string
. Desde main()
pide al usuario dos valores; detecta si son enteros, reales o palabras (sin espacios) y muestra el resultado usando la versión de calcular
adecuada.Código:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
// Sobrecargas
int calcular(int a, int b) { return a + b; }
double calcular(double a, double b) { return a + b; }
string calcular(const string& a, const string& b) { return a + b; }
bool esEntero(const string& s) {
if (s.empty()) return false;
size_t i = (s[0] == '-' || s[0] == '+') ? 1 : 0;
for (; i < s.size(); ++i) if (!isdigit(s[i])) return false;
return true;
}
bool esReal(const string& s) {
stringstream ss(s);
double d;
char c;
return (ss >> d) && !(ss >> c); // true si se leyó un double y no sobra nada
}
int main() {
string x, y;
cout << "Introduce dos valores: ";
cin >> x >> y;
if (esEntero(x) && esEntero(y)) {
cout << "Suma entera: "
<< calcular(stoi(x), stoi(y)) << endl;
} else if (esReal(x) && esReal(y)) {
cout << "Suma real: "
<< calcular(stod(x), stod(y)) << endl;
} else {
cout << "Concatenación: "
<< calcular(x, y) << endl;
}
return 0;
}
Explicación
- Tres funciones
calcular
con firmas distintas ilustran sobrecarga de funciones. - Funciones auxiliares detectan si las entradas son entero, real o texto.
- Se llaman
stoi
ystod
para convertir cadenas a sus tipos numéricos.
Ejercicio 2 – Gestor de personas con std::string
y namespace
Enunciado
Define un espacio de nombres agenda
que contenga la clase Persona
. La clase guarda nombre (std::string
) y edad, e implementa un método presentarse()
. En main()
crea dos objetos Persona
y llama a sus métodos.
std::string
y namespace
Enunciado
Define un espacio de nombres
agenda
que contenga la clase Persona
. La clase guarda nombre (std::string
) y edad, e implementa un método presentarse()
. En main()
crea dos objetos Persona
y llama a sus métodos.Código:
#include <iostream>
#include <string>
namespace agenda {
class Persona {
std::string nombre;
int edad;
public:
Persona(std::string n, int e) : nombre(n), edad(e) {}
void presentarse() const {
std::cout << "Soy " << nombre
<< " y tengo " << edad << " años.\n";
}
};
} // namespace agenda
int main() {
agenda::Persona p1("Lucía", 28);
agenda::Persona p2("Diego", 35);
p1.presentarse();
p2.presentarse();
return 0;
}
Explicación
- Se usa
namespace agenda
para encapsular la clase y evitar colisiones. std::string
simplifica la gestión de texto sin preocuparse por tamaños ni\0
.- Constructor inicializa miembros; el método
presentarse()
imprime usandocout
.
Ejercicio 3 – Conversor de temperaturas con iostream
y referencias
Enunciado
Implementa dos funciones: celsiusToFahrenheit(double c)
y fahrenheitToCelsius(double f)
que devuelvan la temperatura convertida. Pide al usuario elegir el sentido de la conversión y mostrar el resultado con iostream
.
iostream
y referenciasEnunciado
Implementa dos funciones:
celsiusToFahrenheit(double c)
y fahrenheitToCelsius(double f)
que devuelvan la temperatura convertida. Pide al usuario elegir el sentido de la conversión y mostrar el resultado con iostream
.Código:
#include <iostream>
using namespace std;
// Funciones de conversión
double celsiusToFahrenheit(const double& c) { return c * 9.0/5 + 32; }
double fahrenheitToCelsius(const double& f) { return (f - 32) * 5.0/9; }
int main() {
char opcion;
double valor;
cout << "Conversor de temperatura\n";
cout << "a) Celsius -> Fahrenheit\n";
cout << "b) Fahrenheit -> Celsius\n";
cout << "Elige opción (a/b): ";
cin >> opcion;
if (opcion == 'a' || opcion == 'A') {
cout << "Introduce grados Celsius: ";
cin >> valor;
cout << "Fahrenheit: "
<< celsiusToFahrenheit(valor) << endl;
} else if (opcion == 'b' || opcion == 'B') {
cout << "Introduce grados Fahrenheit: ";
cin >> valor;
cout << "Celsius: "
<< fahrenheitToCelsius(valor) << endl;
} else {
cout << "Opción no válida.\n";
}
return 0;
}
Explicación
- Se utiliza
iostream
(cin
/cout
) para I/O en lugar descanf
/printf
. - Las funciones reciben sus argumentos por referencia constante para evitar copias innecesarias y muestran la sintaxis de C++ moderno.
- El programa demuestra control de flujo y operaciones simples con funciones separadas para cada conversión.
Bibliografía del tutorial de C/C++.
- C/C++. Curso de programación. Autor: Miguel Angel Acera (Editorial: Anaya Multimedia)
- C/C++. Curso de programación. Autor: Francisco José Ceballos (Editorial: RA-MA)
- Un recorrido por C++. Autor Bjarne Stroustrup (Editorial: Anaya Multimedia)
- 115 Ejercicios resueltos de programación C++. Autor Jorge Fernando Betancourt e Inma Yolanda Polanco (Editorial: RA-MA)