Curso de C++ Básico. Programación Orientada a Objetos [Parte 5]

Valeo08

Capullo perro no mucho
Miembro del equipo
Moderador
Noderador
Nodero
Noder
24 Dic 2018
1.944
795
24
Panturria
Nodocoins
494
------------------------------------------------------------------------------
5. Diseño Descendente
------------------------------------------------------------------------------

5.1. Programación Estructurada

Un programa está formada de 1 a n funciones. Una Función o subprograma es un conjunto
de acciones agrupadas con un nombre común. Las funciones se escriben una sola vez y pueden ser
utilizada varias veces en distintas partes mediante llamadas, lo que hace que el programa sea
más corto, legible y fácil de corregir, luego disminuye los posibles errores en el código. Las
principales ventajas del uso de las funciones son: la modularización, el ahorro de memoria y
tiempo y la independencia de los datos, además del ocultamiento de la información.

Un programa en C++, está compuesto al menos por una función (main), pudiendo tener además
tantas funciones o clases como se quiera.

C++:
#include <iostream>
#include <string.h>
using namespace std;

#define LONGITUD 12
typedef char cadena[50];

class texto {
    cadena palabras[LONGITUD];
public:
    void leer();
    void buscar();
};

void texto::leer() {
    for (int i = 0; i < LONGITUD; i++) {
        cout << "Introduce la palabra " << i << ": \n";
        cin >> palabras[i];
    }
}

void texto::buscar() {
    cadena buscada;
    bool encontrada = false;
    int i = 0;

    cout << "Introduce la palabra que quieras buscar: ";
    cin >> buscada;

    while (!encontrada && i < LONGITUD) {
        if (strcmp(palabras[i], buscada) == 0) {
            encontrada = true;
        } else {
            i++;
        }
    }

    if (encontrada)
        cout << "\nSe ha encontrado en la posicion " << i;
    else
        cout << "\nNo se ha encontrado esa palabra";
}

int main () {
    texto text;
    texto texts[2]:

    text.leer();
    text.buscar();

    for (int i = 0; i < 2; i++) {
        texts[i].leer();
    }

    return 0;
}

Como vemos en el ejemplo de arriba, las funciones están declaradas arriba y se utilizan abajo
(son llamadas) varias veces. Otra opción sería declarar la función antes del main y luego seguir definiéndola debajo; de este modo:

C++:
#include <iostream>
#include <string.h>
using namespace std;

double alCuadrado();

int main() {
    double valor;
    cout << "Calcular el valor de una base al cuadrado:\n";
    valor = alCuadrado();
    cout << "El resultado es " << valor << "\n";
    return 0;
}

double alCuadrado() {
    double resultado;
    int base;

    cout << "Introduce la base de la potencia: ";
    cin >> base;

    resultado = base * base;
    return resultado;
}
 

Valeo08

Capullo perro no mucho
Miembro del equipo
Moderador
Noderador
Nodero
Noder
24 Dic 2018
1.944
795
24
Panturria
Nodocoins
494
5.2. Variables y sus subtipos

5.2.1. Variables Globales y Locales

Existen, principalmente, dos tipos de variables, las locales y las globales.

Las Variables Locales están declaradas dentro de un bloque o función. Solo se pueden
usar en ese bloque al que pertenece y no se puede acceder a ella desde fuera. Además solo existe
durante la ejecución del bloque en cuestión, es decir, se crea al entrar en el bloque y se
elimina al salir de él.

Las Variables Globales se declaran fuera de cualquier bloque o función, normalmente
al inicio. Pueden ser usadas en cualquier parte del programa, lo que incluyendo las funciones por
lo que no es necesario pasarlas como parámetros. Existen durante toda la ejecución del programa y
solo se eliminan cuando se cierra.

Un punto a tener en cuenta es que una variable local y otra global pueden tener el mismo identificador.

Si nos fijamos en el ejemplo que puse antes, "valor" es una variable local de la función main, y
"resultado" y "base" son variables locales de la función alCuadrado.


5.2.2. Variables dentro de las clases: los Atributos

Dentro de una clase se pueden declarar dos tipos de variables según la ocultación de datos que se
le dé, de hecho, es muy poco común que dentro de una clase existan atributos públicos, salvo
excepciones como por ejemplo una clase de "Constantes".

Las Variables privadas se declaran en la parte "private" de la clase (aunque por defecto son
privadas, con no incluirlas en la parte "public" basta). No son visibles fuera de la clase, y por tanto
solo pueden usarse para métodos de la misma clase.

Las Variables públicas se declaran en la parte "public" de la clase. Son visibles fuera de su
clase por lo que no es aconsejable su uso. La razón por la que se usa, es para incluirla en métodos de
otras clases o para usarlas en cualquier función o expresión ajena a la clase donde se declaró.

Nota: Como recomendación diría que nunca uséis atributos públicos, ya que si se quiere obtener las
variables de una clase, se usan métodos que la devuelvan, los "getters" o que la editen, los "setters".

5.2.3. Parámetros: por Valor y por Referencia

Para comunicarnos con una función e introducir los posibles valores necesarios para que lleve a cabo
su tarea usamos los parámetros.

La sintaxis para los parámetros en funciones y métodos es esta:

Código:
// Funciones
tipo nombre_funcion (tipo param1, ..., tipo paramN) {
    // algo...
    return valor; // del tipo de la funcion
}

// Métodos
tipo nombre_clase::nombre_metodo (tipo param1, ..., tipo paramN) {
    // algo...
    return valor;
}

Nota: Es importante poner el tipo de cada parámetro, aunque sean iguales, me refiero:

void funcionX (int a, int b, int c, float d); // Bien
void funcionY (int a, b, c, float d); // Mal



Los parámetros serán los valores que se les pase a la función/método cuando se las llame. Cada
parámetro actúa dentro de la función/método como una variables local.
A los parámetros que se incluyen en la llamada de la función/método se les llama parámetros reales
o argumentos. A los que aparecen en la descripción de la función/método se les llama
parámetros formales.

Los parámetros formales se sustituyen por los argumentos cuando se llama a la función/método. Cuando
esto ocurre, estos deben coincidir en número y tipo.

Ejemplos, usando las funciones anteriores:
C++:
 #include <iostream>
 using namespace std;

 double alCuadrado(int base) {
     double resultado;
     resultado = base * base;
     return resultado;
 }

 int main() {
     int a, b;

     cout << "Introduce un numero para la base: ";
     cin >> b;

     a = alCuadrado(b);

     cout << endl << b << " al cuadrado es: " << a;

     return 0;
 }

En el ejemplo, el parámetro formal sería "base", de la función "alCuadrado()", y por tanto, el
parámetro real es "b".
 
Última edición:

Valeo08

Capullo perro no mucho
Miembro del equipo
Moderador
Noderador
Nodero
Noder
24 Dic 2018
1.944
795
24
Panturria
Nodocoins
494
Dentro de los parámetros, además se distinguen dos tipos claro: por valor y por referencia.

Los Parámetros por valor son aquellos en los cuales los cambios producidos dentro de la
función/método no afectan a la variable real que es usada como argumento, por lo tanto es como si
en la llamada se le pasa una copia del valor del argumento. Este tipo de parámetro puede ser una
constante, una variable o una expresión del tipo indicado en el parámetros formal.

Los Parámetros por referencia se distinguen en que los cambios que se producen dentro de la
función/método afectan a la variables que se utiliza de argumento, es decir, en la llamada se le
transfiere la propia variable. El parámetro real solo puede ser, por tanto, una variable, ya que
se va a transferir un valor a ella. Para declarar un parámetro de tipo por referencia, se antepone
el carácter "&" en el prototipo y en la definición de la función.

Los ejemplos de esto:

(*) Solo pondré ejemplo del parámetro por referencia, ya que el ejemplo de parámetros anterior nos
sirve para los parámetros por valor.

C++:
#include <iostream>
using namespace std;

void cambiarValores(int &x, int &y) {
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main() {
    int a, b;

    cout << "Introduce el valor de A: ";
    cin >> a;

    cout << "Introduce el valor de B: ";
    cin >> b;

    cambiarValores(a, b);

    cout << endl << "Ahora A vale " << a << " y B vale " << b;

    return 0;
}

Como vemos, en la función cambiarValores hay dos parámetros por referencia x e y, que al pasarles
en la llamada las variables a y b, intercambia sus valores, y no los de una copia, que sería lo que
se intercambiaría, si el parámetro fuera por valor.


5.3. Constructores y destructores

Los Constructores son métodos que se llaman igual que la clase a la que pertenecen y que
no devuelve ningún tipo, ni siquiera void. No se puede llamar, de hecho es llamado automáticamente
cuando se crea un objeto de la clase, pudiendo tener parámetro. También es un método que se puede
sobrecargar (explico abajo qué es esto). Estos métodos se utilizan para inicializar los atributos
de la clase.

Los Destructores también tienen el mismo nombre de la clase en la que están definidos,
sin embargo, se diferencia de los anteriores en que presenta un carácter ~ antes del nombre. Al
igual que los constructores, no devuelven ningún tipo y son llamados automáticamente cuando el
objeto va dejar de existir. En cambio, el método es único y no tiene parámetros. Se llaman a sí
solos antes de la destrucción del objeto por el colector de basura, por ello, se emplea para
destruir variables dinámicas (un tipo de variables, que algún día explicaré), establecer valores
antes de la eliminación, etc.

Un ejemplo (spoiler, contiene cosas que no he explicado pero que indican para que sirven
los destructores):

C++:
#include <iostream>
using namespace std;

class Jugador() {
    int *objetos; // esto es un atributo dinámico
    int x, y, z;
    float vida;
public:
    Jugador();
    Jugador(int posX, int posY, int posZ);
    void incluirObjeto(); // no voy a implementar el método ya que maneja memoria dinámica
    void devolverPos(int &posX, int &posY, int &posZ);
    void establecerPos(int posX, int posY, int posZ);
}

// Dos constructores sobrecargados
Jugador::Jugador() {
    /*
    Aquí se reserva memoria para la variable objetos
    */

    x = y = z = 0;
    vida = 100;
}

Jugador::Jugador(int posX, int posY, int posZ) {
    /*
    Aquí se reserva memoria para la variable objetos
    */

    x = posX;
    y = posY;
    z = posZ;

    vida = 100;
}

// Un destructor
Jugador::~Jugador() {
    if (objetos != NULL)
        delete[] objetos; // aquí se elimina la memoria dinámica

    // También hay quién establece las variables a cierto valor
    // pero es inútil ya que el colector de basura eliminará el objeto igual,
    // lo único importante es eliminar la memoria dinámica (liberarla).
}

Jugador::devolverPos(int &posX, int &posY, int &posZ) {
    posX = x;
    posY = y;
    posZ = z;
}

Jugador::establecerPos(int posX, int posY, int posZ) {
    x = posX;
    y = posY;
    z = posZ;
}
 

Valeo08

Capullo perro no mucho
Miembro del equipo
Moderador
Noderador
Nodero
Noder
24 Dic 2018
1.944
795
24
Panturria
Nodocoins
494
5.4. Estructuras y Objetos por parámetro. Propiedades

Por defecto, si pasamos como parámetro a una función/método una estructura (excepto los vectores),
se pasan por valor, a no ser que se especifique el símbolo &, que lo hará por referencia.

Los vectores (arrays) siempre se pasan por referencia, no hay que especificar el símbolo &, lo que
hay que tener en cuenta si lo que queríamos era obtener una copia del array por parámetro.

En la llamada de una función ponemos como parámetro el nombre del vector sin los corchetes. En el
caso de que queramos obtener un elemento del array, sí utilizaremos los corchetes, colocando entre
ellos la posición que ocupa dicho elemento dentro del array. Este elemento, se podrá pasar tanto por
valor como por referencia.

5.5. Funciones y Métodos sobrecargados

En C++ (al igual que casi todos los lenguajes de POO) está permitido definir varias funciones o
métodos distintos con el mismo nombre, siempre y cuando, tengan cada una, o distinto número de
argumentos o distinto tipo. Los argumentos de la llamada son lo que indicarán al compilador
qué función es la que se va a usar.

Hay que tener en cuenta varias cosas: si las funciones o métodos solo se diferencian en el tipo
de datos que devuelven, no se pueden sobrecargar. Si tienen distinto tipo de argumentos,
puede sobrecargarse sea o no igual el tipo que devuelve la función.

Por ejemplo:

C++:
// Esto es un ejemplo de como no se podria sobrecargar una función
int suma(int a, int b) {
    return a+b;
}

float suma(int a, int b) {
    return a+b;
}

// Esto sí se podría sobrecargar
float dividir(float dividendo, float divisor) {
    return dividendo/divisor;
}

double dividir(double dividendo, double divisor) {
    return dividendo/divisor;
}

int main() {
    suma(3, 6); // un error porque no hay forma de saber a qué función está llamando

    dividir(13.7, 2.64);
    return 0;
}


Hasta aquí el curso básico.
Lo mismo cuando pille vacaciones, empiezo otro más avanzado. Hasta
entonces, pues os aguantáis, un abrazo cracks.
 
Última edición:
  • Love
Reacciones : Papallusqueti
Arriba Pie