5.5. La clase

El control de acceso se suele llamar también ocultación de la implementación. Incluir funciones dentro de las estructuras (a menudo llamado encapsulación [52]) produce tipos de dato con características y comportamiento, pero el control de acceso pone fronteras en esos tipos, por dos razones importantes. La primera es para establecer lo que el programador cliente puede y no puede hacer. Puede construir los mecanismos internos de la estructura sin preocuparse de que el programador cliente pueda pensar que son parte de la interfaz que debe usar.

Esto nos lleva directamente a la segunda razón, que es separar la interfaz de la implementación. Si la estructura se usa en una serie de programas, y el programador cliente no puede hacer más que mandar mensajes a la interfaz pública, usted puede cambiar cualquier cosa privada sin que se deba modificar código cliente.

La encapsulación y el control de acceso, juntos, crean algo más que una estructura de C. Estamos ahora en el mundo de la programación orientada a objetos, donde una estructura describe una clase de objetos como describiría una clase de peces o pájaros: Cualquier objeto que pertenezca a esa clase compartirá esas características y comportamiento. En esto se ha convertido la declaración de una estructura, en una descripción de la forma en la que los objetos de este tipo serán y actuarán.

En el lenguaje OOP original, Simula-67, la palabra clave class fue usada para describir un nuevo tipo de dato. Aparentemente esto inspiro a Stroustrup a elegir esa misma palabra en C++, para enfatizar que este era el punto clave de todo el lenguaje: la creación de nuevos tipos de dato que son más que solo estructuras de C con funciones. Esto parece suficiente justificación para una nueva palabra clave.

De todas formas, el uso de class en C++ es casi innecesario. Es idéntico a struct en todos los aspectos excepto en uno: class pone por defecto private, mientras que struct lo hace a public. Estas son dos formas de decir lo mismo:

//: C05:Class.cpp
// Similarity of struct and class

struct A {
private:
  int i, j, k;
public:
  int f();
  void g();
};

int A::f() { 
  return i + j + k; 
}

void A::g() { 
  i = j = k = 0; 
}

// Identical results are produced with:

class B {
  int i, j, k;
public:
  int f();
  void g();
};

int B::f() { 
  return i + j + k; 
}

void B::g() { 
  i = j = k = 0; 
} 

int main() {
  A a;
  B b;
  a.f(); a.g();
  b.f(); b.g();
} ///:~

Listado 5.5. C05/Class.cpp


La clase (class) en un concepto OOP fundamental en C++. Es una de la palabras clave que no se pondrán en negrita en este libro - es incomodo pues se repite mucho. El cambio a clases es tan importante que sospecho que Stroustrup hubiese preferido eliminar completamente struct, pero la necesidad de compatibilidad con C no lo hubiese permitido.

Mucha gente prefiere crear clases a la manera struct en vez de a la mánera class, pues sustituye el «por-defecto-private» de class empezando con los elementos public:

class X {
public:
    void miembro_de_interfaz();
private:
    void miembro_privado();
    int representacion_interna;
};

El porqué de esto es que tiene más sentido ver primero lo que más interesa, el programador cliente puede ignorar todo lo que dice private. De hecho, la única razón de que todos los miembros deban ser declarados en la clase es que el compilador sepa como de grande son los objetos y pueda colocarlos correctamente, garantizando así la consistencia.

De todas formas, los ejemplos en este libro pondrán los miembros privados primero, así:

class X {
    void private_function();
    int internal_representation;
public:
    void interface_function();
};

Alguna gente incluso decora sus nombres privados

class Y {
public:
    void f();
private:
    int mX;  // "Self-decorated" name
};

Como mX esta ya oculto para Y, la m (de «miembro») es innecesaria. De todas formas, en proyectos con muchas variables globales (algo que debe evitar a toda costa, aunque a veces inevitable en proyectos existentes), es de ayuda poder distinguir variables globales de atributos en la definición de los métodos.

5.5.1. Modificaciones en Stash para usar control de acceso

Tiene sentido coger el ejemplo del capítulo 4 y modificarlo para usar clases y control de acceso. Dese cuenta de cómo la parte de la interfaz a usar en la programación cliente está claramente diferenciada, así no hay posibilidad de que el programador cliente manipule accidentalmente parte de la clase que no debería.

//: C05:Stash.h
// Converted to use access control
#ifndef STASH_H
#define STASH_H

class Stash {
  int size;      // Size of each space
  int quantity;  // Number of storage spaces
  int next;      // Next empty space
  // Dynamically allocated array of bytes:
  unsigned char* storage;
  void inflate(int increase);
public:
  void initialize(int size);
  void cleanup();
  int add(void* element);
  void* fetch(int index);
  int count();
};
#endif // STASH_H ///:~

Listado 5.6. C05/Stash.h


La función inflate() se ha hecho private porque solo es usada por la función add() y por tanto es parte de la implementación interna, no de la interfaz. Esto significa que, más tarde, puede cambiar la implementación interna para usar un sistema de gestión de memoria diferente.

Aparte del nombre del archivo include, la cabecera de antes es lo único que ha sido cambiado para este ejemplo. El fichero de implementación y de prueba son los mismos.



[52] Como se dijo anteriormente, a veces el control de acceso se llama también encapsulación