Access specifiers
En una member-specification de una class/struct o union , define la accesibilidad de los miembros subsiguientes.
En una especificación-de-base de una declaración de clase derivada , define la accesibilidad de los miembros heredados de la clase base subsecuente.
Contenidos |
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) | ||||||||
Los miembros privados de la clase base siempre son inaccesibles para la clase derivada, independientemente de la herencia pública, protegida o privada.
Explicación
El nombre de cada class miembro (static, non-static, function, type, etc) tiene un "acceso de miembro" asociado. Cuando se utiliza el nombre del miembro en cualquier parte de un programa, se verifica su acceso, y si no satisface las reglas de acceso, el programa no compila:
#include <iostream> class Example { public: // todas las declaraciones después de este punto son públicas void add(int x) // el miembro "add" tiene acceso público { n += x; // OK: Example::n privado puede ser accedido desde Example::add } private: // todas las declaraciones después de este punto son privadas int n = 0; // el miembro "n" tiene acceso privado }; int main() { Example e; e.add(1); // OK: Example::add público puede ser accedido desde main // e.n = 7; // error: Example::n privado no puede ser accedido desde main }
Los especificadores de acceso otorgan al autor de la clase la capacidad de decidir qué miembros de la clase son accesibles para los usuarios de la clase (es decir, la interfaz ) y qué miembros son para uso interno de la clase (la implementación ).
En detalle
Todos los miembros de una clase (cuerpos de member functions , inicializadores de objetos miembro, y todas las nested class definitions ) tienen acceso a todos los nombres a los que la clase puede acceder. Una clase local dentro de una función miembro tiene acceso a todos los nombres a los que la función miembro puede acceder.
Una clase definida con la palabra clave
class
tiene acceso privado para sus miembros y sus clases base por defecto. Una clase definida con la palabra clave
struct
tiene acceso público para sus miembros y sus clases base por defecto. Una
union
tiene acceso público para sus miembros por defecto.
Para otorgar acceso a funciones o clases adicionales a miembros protegidos o privados, puede utilizarse una declaración de amistad .
La accesibilidad se aplica a todos los nombres sin importar su origen, por lo que un nombre introducido por un typedef o using declarations (excepto los constructores heredados) es verificado, no el nombre al que se refiere:
class A : X { class B {}; // B es privado en A public: typedef B BB; // BB es público }; void f() { A::B y; // error: A::B es privado A::BB x; // OK: A::BB es público }
El acceso a miembros no afecta la visibilidad: los nombres de miembros privados y heredados de forma privada son visibles y considerados por la resolución de sobrecarga, las conversiones implícitas a clases base inaccesibles aún se consideran, etc. La verificación de acceso a miembros 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 verificación de acceso para los nombres utilizados en argumentos de función predeterminados así como en los parámetros de plantilla predeterminados se realiza en el punto de declaración, no en el punto de uso.
Las reglas de acceso para los nombres de las virtual functions se verifican en el punto de llamada utilizando el tipo de la expresión usada para denotar el objeto para el cual se llama a la función miembro. Se ignora el acceso del final overrider:
struct B { virtual int f(); // f es público en B }; class D : public B { private: int f(); // f es privado en D }; void f() { D d; B& b = d; b.f(); // OK: B::f es público, D::f se invoca aunque sea privado d.f(); // error: D::f es privado }
Un nombre que es privado según la búsqueda de nombre no calificada , puede ser accesible mediante la búsqueda de nombre calificada:
class A {}; class B : private A {}; class C : public B { A* p; // error: la búsqueda de nombre no calificada encuentra A como la base privada de B ::A* q; // OK: la búsqueda de nombre calificada encuentra la declaración a nivel de namespace };
Un nombre que es accesible a través de múltiples rutas en el grafo de herencia tiene el acceso de la ruta con mayor nivel de 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(); // OK: W es accesible para C a través de B } };
Cualquier número de especificadores de acceso puede aparecer dentro de una clase, en cualquier orden.
|
Los especificadores de acceso de miembros pueden afectar el diseño de la clase : las direcciones de los miembros de datos no estáticos solo están garantizadas de aumentar 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) . |
(hasta C++23) |
|
Para tipos de diseño estándar , todos los miembros de datos no estáticos deben tener el mismo acceso. |
(desde C++11) |
Cuando un miembro se vuelve a declarar dentro de la misma clase, debe hacerlo bajo el mismo acceso de miembro:
struct S { class A; // S::A es público private: class A {}; // error: no se puede cambiar el acceso };
Acceso público a miembros
Los miembros públicos forman parte de la interfaz pública de una clase (otras partes de la interfaz pública son las funciones no miembro encontradas mediante 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 }
Acceso a miembros protegidos
Los miembros protegidos forman la interfaz de una clase para sus clases derivadas (lo cual es distinto de la interfaz pública de la clase).
Un miembro protegido de una clase solo es accesible
struct Base { protected: int i; private: void g(Base& b, struct Derived& d); }; struct Derived : Base { friend void h(Base& b, Derived& d); void f(Base& b, Derived& d) // función miembro de una clase derivada { ++d.i; // OK: el tipo de d es Derived ++i; // OK: el tipo del '*this' implícito es Derived // ++b.i; // error: no se puede acceder a un miembro protegido a través // de Base (de lo contrario sería posible cambiar // otras clases derivadas, como una hipotética // Derived2, implementación base) } }; void Base::g(Base& b, Derived& d) // función miembro de Base { ++i; // OK ++b.i; // OK ++d.i; // OK } void h(Base& b, Derived& d) // Amigo de Derived { ++d.i; // OK: el amigo de Derived puede acceder a un miembro // protegido a través de un objeto de Derived // ++b.i; // error: el amigo de Derived no es amigo de Base } void x(Base& b, Derived& d) // no miembro no amigo { // ++b.i; // error: sin acceso desde no miembro // ++d.i; // error: sin acceso desde no miembro }
Cuando se forma un puntero a un miembro protegido, debe usar una clase derivada en su declaración:
struct Base { protected: int i; }; struct Derived : Base { void f() { // int Base::* ptr = &Base::i; // error: debe nombrarse usando Derived int Base::* ptr = &Derived::i; // OK } };
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 para los miembros y amigos de esa clase, independientemente de si los miembros están en la misma instancia o en instancias diferentes:
class S { private: int n; // S::n es privado public: S() : n(10) {} // this->n es accesible en S::S S(const S& other) : n(other.n) {} // other.n es accesible en S::S };
La conversión explícita (estilo C y estilo función) permite convertir desde un lvalue derivado a referencia de su base privada, o desde un puntero a derivado a un puntero a su base privada.
Herencia
Consulte clases derivadas para el significado de la herencia public, protected y private.
Palabras clave
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 1873 | C++98 | los miembros protected eran accesibles para friends de clases derivadas | hecho inaccesible |