Programación orientada a objetos
Clases e instancias
El objetivo perseguido por Bjarne Stroustrup era, recordemos, implementar en un compilador C las clases descritas por el lenguaje Simula. Siendo estos dos últimos lenguajes radicalmente opuestos en su planteamiento, era necesario identificar una doble continuidad, en particular del lado de C.
Era fácil observar que la programación se simplificaría mucho si ciertas funciones pudieran migrar dentro de las estructuras de C. Por lo tanto, ya no habría estructura para pasar a estas funciones, ya que se aplicarían, obviamente, a los campos de la estructura.
Sin embargo, teníamos que mantener una forma de distinguir dos instancias y por eso cambiamos la sintaxis del operador punto:
Programación funcional |
Programación orientada a objetos |
|
|
Esta diferencia de enfoque tiene varias consecuencias positivas para la programación. Para empezar, el programador ya no tiene que elegir entre los diferentes modos de pasar de la estructura a la función mostrar(). Entonces, podremos hacer una distinción entre los elementos de primer y segundo plano (campos, funciones). Los de primer plano serán visibles, accesibles fuera de la estructura. El resto estarán ocultos, serán inaccesibles.
Este proceso garantiza una gran independencia en la implementación de un concepto, lo que también induce una buena estabilidad de los desarrollos.
1. Definición de clase
Por tanto, una clase es una estructura que tiene tanto campos como funciones. Cuando las funciones se consideran dentro de una clase, se les da el nombre de métodos.
Todos los campos y métodos se denominan miembros. No recomendamos nombrar campos usando el término atributo, porque puede adquirir un significado muy especial en el lenguaje C++ administrado o en C#.
Para el lector que cambia de Java a C++, preste atención a terminar la declaración de una clase con el carácter de punto y coma, siendo una clase la continuación del concepto de estructura:...
Herencia
1. Derivación de clase (herencia)
Ahora que conocemos bien la estructura y el funcionamiento de una clase, vamos a hacer que nuestros programas sean más genéricos. Es común describir un problema general con algoritmos apropiados y luego hacer pequeños cambios cuando surge un caso similar.
La filosofía orientada a objetos consiste en limitar al máximo las macros, las inclusiones y los módulos. Esta forma de abordar las cosas presenta muchos riesgos a medida que aumenta la complejidad de los problemas. La programación orientada a objetos se expresa más bien en un eje genérico/específico, mucho más adaptado a las pequeñas variaciones de los datos de un problema. Los métodos de modelado basados en UML pueden guiarle para construir redes de clases adaptadas a las circunstancias de un proyecto. Pero también es necesario basarse en los lenguajes que soportan este enfoque, y C++ es uno de ellos.
a. Ejemplo de derivación de clases
Imaginemos una clase Cuenta, compuesta de los siguientes elementos:
class Cuenta
{
protected:
int numero; // numero de la cuenta
double saldo; // saldo de la cuenta
static int num; // variable utilizada para calcular el siguiente número
static int siguiente_numero();
public:
char*titular; // titular de la cuenta
Cuenta(char*titular);
~Cuenta(void);
void Acredito(double cantidad);
bool Adebito(double cantidad);
void incrementar();
};
Ahora podemos imaginar una clase CuentaRemunerada, que especializa el funcionamiento de la clase Cuenta. Es fácil imaginar que una cuenta remunerada generalmente admite las mismas operaciones que una cuenta convencional, modificándose levemente su comportamiento con respecto a la operación de crédito, ya que un interés lo paga la entidad bancaria. Como resultado, es tedioso querer reescribir completamente el programa que funciona para la clase Cuenta. En su lugar, derivaremos esta última clase para obtener la clase CuentaRemunerada.
La clase CuentaRemunerada, por su parte, toma todas las características...
Otros aspectos de la POO
1. Conversión dinámica
a. Conversiones desde otro tipo
Los constructores le permiten convertir objetos a partir de instancias (valores) expresados en otro tipo.
Tomemos el caso de nuestra clase Cadena. Sería interesante "convertir" un char* o un char en una cadena:
#include <string.h>
class Cadena
{
private:
char*buffer;
int t_buf;
int longitud;
public:
// un constructor por defecto
Cadena()
{
t_buf=100;
buffer=new char[t_buf];
longitud=0;
}
Cadena (int t_buf)
{
this->t_buf=t_buf;
buffer=new char[t_buf];
longitud=0;
}
Cadena(char c)
{
t_buf=1;
longitud=1;
buffer=new char[t_buf];
buffer[0]=c;
}
Cadena(char*s)
{
t_buf=strlen(s)+1;
buffer=new char[t_buf];
longitud=strlen(s);
strcpy(buffer,s);
}
void mostrar()
{
for(int i=0; i<longitud; i++)
printf("%c",buffer[i]);
printf("\n");
}
} ;
int main(int argc, char* argv[])
{
// Escritura 1
// Conversión por el uso explícito del constructor
Cadena x;
x=Cadena("hola"); // conversión
x.mostrar();
// Escritura 2
// Cast cohercitivo
Cadena y=(char*) "hola"; // conversión (cast)
y.mostrar();
// Escritura 3
// Cast enfoque C++
Cadena z=char('A'); //...
Trabajos prácticos
1. Utilización de la herencia de clases en el intérprete tiny-lisp
En el intérprete tiny-lisp, las clases derivadas de ScriptBox son responsables de hacer que el analizador funcione en muchos entornos:
-
Sandbox es un entorno aislado donde se neutralizan las entradas-salidas (no se activan).
-
El entorno ConsoleBox proporciona entradas-salidas en la consola del sistema.
La clase base se llama ScriptBox y define un método virtual init_events():
/*
Entorno de ejecución genérico
*/
class ScriptBox
{
public:
LexScan*lexScan;
Evaluator*parser;
ScriptBox();
~ScriptBox();
virtual void init_events();
void new_text();
void set_text(string text);
void parse();
bool has_errors();
string get_errors();
string get_text();
bool set_debug();
};
En la versión neutra, Sandbox, este método no está sobrecargado.
/*
Entorno de ejecución neutro
*/
class Sandbox :
public ScriptBox
{
public:
Sandbox();
~Sandbox();
};
Por el contrario, en el caso del entorno Consolebox, el método está sobrecargado para declarar eventos de tipo entradas-salidas en la consola (pantalla, teclado).
/*
Entorno de ejecución Consola
*/ ...