Especificadores de acceso
En una especificación-de-miembro de una clase, estructura o unión (class/struct o union), define la accesibilidad de los miembros subsecuentes.
En un especificador-de-base de una declaración de clase derivada, define la accesibilidad de los miembros heredados de la clase base subsecuente.
Contenido |
[editar] Sintaxis
public : declaraciones-de-miembros
|
(1) | ||||||||
protected : declaraciones-de-miembros
|
(2) | ||||||||
private : declaraciones-de-miembros
|
(3) | ||||||||
public clase-base | (4) | ||||||||
protected clase-base | (5) | ||||||||
private clase-base | (6) | ||||||||
[editar] Explicación
El nombre de cada miembro de clase (estático, no-estático, función, tipo, etc.) tiene un "acceso a miembro" asociado. Cuando el nombre del miembro se usa en cualquier parte del programa, se comprueba su acceso, y si no satisface las reglas de acceso, el programa no compila:
#include <iostream> class Ejemplo { public: // todas las declaraciones después de este punto son públicas void agregar(int x) // miembro "agregar" tiene acceso público { n += x; // de acuerdo: private Ejemplo::n puede ser accedido desde Ejemplo::agregar } private: // todas las declaraciones después de este punto son privadas int n = 0; // miembro "n" tiene acceso privado }; int main() { Ejemplo e; e.agregar(1); // de acuerdo: public Ejemplo::agregar puede ser accedido desde main // e.n = 7; // ERROR: private Ejemplo::n no puede ser accedido desde main }
Los especificadores de acceso le dan al autor de la clase la habilidad de decidir que miembros de clase son accesibles a los usuarios de la misma (es decir, la interfaz) y que miembros son para uso interno de la clase (la implementación).
[editar] En detalle
Todos los miembros de una clase (cuerpos de funciones miembro, inicializadores de objetos miembro, y toda las definiciones de clases anidadas) tienen acceso a todos los nombres que son accesibles a la clase. Una clase local dentro de una función miembro tiene acceso a todos los nombres accesibles a la función miembro.
Una clase definida con la palabra clave class
tiene por defecto acceso privado para sus miembros y clases bases. Una clase definida con la palabra clave struct
tiene por defecto acceso público para sus miembros y clases base. Una unión tiene por defecto acceso público para sus miembros.
Para permitir acceso a funciones o clases adicionales a miembros protegidos o privados, se puede usar una declaración de amistad.
La accesibilidad se aplica a todos los nombres sin tener en cuenta su origen, por lo que se comprueba un nombre introducido por typedef o declaraciones using (excepto los constructores heredados), no el nombre al que se refiere:
class A : X { class B {}; // B es privado en A public: typedef B BB; // BB es pública }; void f() { A::B y; // error: A::B es privado A::BB x; // correcto: A::BB es pública }
El acceso a miembro no afecta a la visibilidad: los nombre de miembros privados y heredados de forma privada son visibles y se tienen en cuenta para la resolución de sobrecarga, las conversiones implícitas a clase bases inaccesibles aún se consideran, etc. La verificación de acceso a miembro es el último paso después de interpretar cualquier construcción del lenguaje. La intención de esta regla es que reemplazar cualquier private
con public
nunca altere el comportamiento del programa.
La comprobación de acceso a los nombres usados en los argumentos de función por defecto, así como en los parámetros de plantilla por defecto, se realiza en el punto de declaración, no en el punto de uso.
Las reglas de acceso para los nombres de funciones virtuales se comprueban en el punto de llamada usando el tipo de expresión utilizada para indicar el objeto para el que se llama a la función miembro. Se ignora el acceso del reemplazo final:
struct B { virtual int f(); // f es pública en B }; class D : public B { private: int f(); // f es privada en D }; void f() { D d; B& b = d; b.f(); // correcto: B::f es pública, se invoca a D::f aunque es privada d.f(); // error: D::f es privada }
Un nombre que es privado de acuerdo con la búsqueda de nombres sin calificar, podría ser accesible a través de la búsqueda de nombres calificados:
class A {}; class B : private A {}; class C : public B { A* p; // error: la búsqueda de nombres sin calificar encuentra A como base // privada de B ::A* q; // correcto: la búsqueda de nombres calificada encuentra la declaración // a nivel de espacio de nombres };
Un nombre que es accesible a través de varias rutas en el esquema de herencia tiene el acceso de la ruta con el mayor acceso:
class W { public: void f(); }; class A : private virtual W {}; class B : public virtual W {}; class C : public A, public B { void f() { W::f(); // correcto: W es accesible a C a través de B } };
Dentro de una clase puede aparecer cualquier número de especificadores de acceso, en cualquier orden. Los especificadores de acceso a miembros pueden afectar al diseño de la clase: solo se garantiza que las direcciones de los miembros de datos no estáticos aumenten en orden de declaración para los miembros no separados por un especificador de acceso (hasta C++11)con el mismo acceso (desde C++11).
Para los tipos de diseño estándar, todos los miembros de datos no estáticos deben tener el mismo acceso. |
(desde C++11) |
Cuando se redeclara un miembro dentro de la misma clase, debe hacerse con el mismo acceso a miembro:
struct S { class A; // S::A es público private: class A {}; // error: no se puede cambiar el acceso };
[editar] Acceso a miembro público
Los miembros públicos forman parte de la interfaz pública de una clase (otras partes de la interfaz pública son las funciones que no miembros encontradas por ADL).
Un miembro público de una clase es accesible en cualquier lugar:
class S { public: // n, E, A, B, C, U, f son miembros públicos int n; enum E {A, B, C}; struct U {}; static void f() {} }; int main() { S::f(); // S::f es accesible en main S s; s.n = S::B; // S::n y S::B son accesibles en main S::U x; // S::U es accesible en main }
[editar] Acceso a miembro protegido
Los miembros protegidos forman la interfaz de una clase con sus clases derivadas (que es distinta de la interfaz pública de la clase).
Un miembro protegido de una clase es accesible solamente
struct Base { protected: int i; private: void g(Base& b, struct Derivada& d); }; struct Derivada : Base { void f(Base& b, Derivada& d) // función miembro de una clase derivada { ++d.i; // correcto: el tipo de d es Derivada ++i; // correcto: el tipo del '*this' implícito es Derivada // ++b.i; // error: no se puede acceder a in miembro protegido a // través de Base (por contra, sería posible modificar // otras clases derivadas, como una hipotética // implementación de base Derivada2) } }; void Base::g(Base& b, Derivada& d) // función miembro de Base { ++i; // correcto ++b.i; // correcto ++d.i; // correcto } void x(Base& b, Derivada& d) // no miembro, no amiga { // ++b.i; // error: no acceso desde no miembro // ++d.i; // error: no acceso desde no miembro }
Cuando se forma un puntero a un miembro protegido, debe usarse una clase derivada en su declaración:
struct Base { protected: int i; }; struct Derivada : Base { void f() { // int Base::* ptr = &Base::i; // error: se debe nombrar usando Derivada int Base::* ptr = &Derivada::i; // correcto } };
[editar] Acceso a miembros privados
Los miembros privados forman la implementación de una clase, así como la interfaz privada para los otros miembros de la clase.
Un miembro privado de una clase solo es accesible a los miembros y amigas de esta clase, independientemente de si los miembros están en la misma o en diferentes instancias:
class S { private: int n; // S::n es privada public: S() : n(10) {} // this->n es accesible en S::S S(const S& otra) : n(otra.n) {} // otra.n es accesible en S::S };
La conversión explícita (estilo C y estilo función) permite convertir desde un lvalue derivado a una referencia a su base privada, o desde un puntero a la derivada a un puntero a su base privada.
[editar] Herencia
Para el significado de herencia pública, protegida y privada, véase clases derivadas.
[editar] Informe de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
CWG 1873 | C++98 | los miembros protegidos eran accesibles para amigas de clases derivadas | se hace inaccesible |