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 [55]) 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 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. Ahora está 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 cómo serán y cómo se comportarán todos los objetos de este tipo.
En el lenguaje OOP original, Simula-67, la palabra reservada class fue usada para describir un nuevo tipo de dato. Aparentemente esto inspiró a Stroustrup a elegir esa misma palabra en C++, para enfatizar que éste era el punto clave de todo el lenguaje: la creación de nuevos tipos de dato que son más que sólo estructuras de C con funciones. Esto parece suficiente justificación para una nueva palabra reservada.
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. Éstas 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) es un concepto OOP fundamental en C++. Es una de las palabras reservadas que no se pondrán en negrita en este libro; resulta molesto con una palabra que se repite tanto como «class». 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 manera class, pues así se anula el comportamiento «private por defecto» 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 que el lector vea primero lo que más le interesa; después puede ignorar todo lo que dice private. De hecho, las únicas razones por las que el resto de miembros deben declararse en la clase son que el compilador sepa qué tamaño tienen los objetos y pueda ubicarlos correctamente, y que pueda garantizar la coherencia.
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(); };
Hay quien incluso se toma la molestia de decorar sus nombres privados:
class Y { public: void f(); private: int mX; // "Self-decorated" name };
Como mX ya está oculto en el ámbito de Y, la m (de «miembro») es innecesaria. De todas formas, en proyectos con muchas variables globales (algo que debe intentar evitar, aunque a veces es inevitable en proyectos existentes), es de ayuda poder distinguir variables globales de atributos en la definición de los métodos.
Tiene sentido coger los ejemplos del Capítulo 4 y modificarlos 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 sólo la usa 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 fichero incluido, la cabecera de antes es lo único que ha cambiado en este ejemplo. Los ficheros de implementación y de prueba son los mismos.