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.
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.