Introducción a la Programación Orientada a Objetos en C++
La Programación Orientada a Objetos (POO) es un paradigma de programación que organiza el código en términos de «clases de objetos». Estos objetos son entidades que combinan estado (propiedades o datos, atributos), comportamiento (procedimientos o métodos) e identidad (propiedad única del objeto que lo distingue de los demás).
Diferencias entre POO y Programación Estructurada
La diferencia fundamental entre la POO y la programación estructurada radica en la organización del código. En la programación estructurada, los datos y los procedimientos están separados y sin relación directa. En cambio, en la POO, primero se definen objetos que encapsulan datos y métodos, y luego se les envían mensajes para que ejecuten sus métodos.
Conceptos Fundamentales de la POO
- Clase: Define las propiedades y el comportamiento de un tipo de objeto. La instanciación es el proceso de crear un objeto a partir de la definición de una clase.
- Objeto: Es una instancia de una clase. Un objeto posee un conjunto de propiedades o atributos (datos) y de comportamientos o funcionalidades (métodos).
- Atributos: Son las características que definen a una clase.
- Método: Es un algoritmo asociado a un objeto, cuya ejecución se desencadena al recibir un «mensaje».
Características de la POO
- Abstracción: Permite seleccionar las características relevantes de un conjunto e identificar comportamientos comunes para simplificar la complejidad.
- Polimorfismo: Las referencias y colecciones de objetos pueden contener objetos de diferentes tipos. La invocación de un comportamiento en una referencia produce el comportamiento correcto según el tipo real del objeto referenciado.
- Herencia: Las clases no están aisladas, sino que se relacionan entre sí, formando una jerarquía. Los objetos heredan las propiedades y el comportamiento de todas las clases a las que pertenecen.
Mejoras en Funciones de C++
- Parámetros Predeterminados: Si no se especifica un valor para un parámetro, se utiliza el valor predefinido.
- Sobrecarga: Permite definir varias funciones con el mismo nombre, diferenciándolas por el número y/o tipo de sus argumentos.
- Parámetros por Referencia: Usando un modificador constante (
const
) adecuadamente, se evita la duplicación de objetos. El uso de parámetros por referencia es más legible que el uso de punteros.
Miembros de una Clase
Los miembros de una clase (métodos o atributos) pueden ser de varios tipos:
- Público (public): Accesible desde el interior de la clase, desde una subclase y desde un objeto instanciado de cualquiera de estas.
- Privado (private): Accesible solo desde el interior de la clase que lo posee.
- Protegido (protected): Accesible desde el interior de la clase y desde las clases derivadas (subclases).
Normalmente, los atributos de una clase se declaran como privados y los métodos como públicos.
Definición de Métodos
Los métodos se pueden definir:
- En la propia declaración de la clase (inline): Recomendado solo para métodos sencillos.
- Fuera de la declaración de la clase: La declaración de la clase se coloca en un fichero
.hpp
, y la definición de los métodos en un fichero.cpp
.
Memoria Dinámica
En C++, se utilizan las palabras reservadas new
para reservar memoria dinámica y delete
para liberar la memoria reservada con new
. Los atributos son dinámicos cuando se declaran como punteros dentro de una clase. Es necesario crear estos atributos en el constructor.
Los operadores de asignación para atributos dinámicos son imprescindibles para evitar la compartición de datos y la pérdida de memoria. Con el operador de asignación, se copia el contenido de los punteros, no los punteros en sí.
Herencia
La clase madre puede ser public
, private
o protected
. La clase hija hereda todos los atributos de la madre, pero solo puede acceder a los públicos y protegidos. La clase hija hereda todos los métodos de la madre, pero solo puede acceder a los públicos y protegidos. El primer constructor que se debe llamar es el de la clase madre, seguido por el de las clases hijas. De forma inversa, el primer destructor que se debe llamar es el de la clase hija, seguido por el de la madre. Si los métodos son públicos o protegidos, se pueden modificar en la clase hija, siempre y cuando se mantenga la misma signatura y el mismo tipo de dato devuelto.
Si un método público definido en la clase madre no se define en la clase hija, no se puede utilizar desde la clase hija (ocultación).
Polimorfismo
Es la propiedad de C++ que permite acceder a objetos de una clase derivada usando un puntero a la clase base.
El uso de funciones virtuales es necesario para que, al invocar una función que se ha redefinido en la clase derivada, se llame a la versión de la clase derivada. Esto se logra con la palabra reservada virtual
. Si en una clase existen funciones virtuales, el destructor debe ser virtual. Los constructores no pueden ser virtuales.
Clases Abstractas
Actúan como expresiones de conceptos generales de los que se pueden derivar clases más concretas. No se puede crear un objeto de un tipo de clase abstracta, aunque se pueden utilizar punteros y referencias a tipos de clase abstracta.
Plantillas (Templates)
Las plantillas se utilizan para crear código genérico, donde el tipo de datos es un parámetro. Las plantillas pueden ser de:
- Funciones: Para algoritmos genéricos (ordenación, búsqueda, etc.). Sintaxis:
template <parámetros plantilla> definición función;
- Clases: Para estructuras de datos genéricas (pila, cola, vector, etc.). Sintaxis:
template <parámetros plantilla> definición clase;