Namespaces
Variants

Access specifiers

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Access specifiers
friend specifier

Class-specific function properties
Special member functions
Templates
Miscellaneous

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)
1) Los miembros declarados después del especificador de acceso tienen acceso público como miembros.
2) Los miembros declarados después del especificador de acceso tienen acceso de miembro protegido.
3) Los miembros declarados después del especificador de acceso tienen acceso de miembro privado.
4) Herencia pública : los miembros públicos y protegidos de la clase base listados después del especificador de acceso mantienen su acceso de miembro en la clase derivada.
5) Protected inheritance : los miembros públicos y protegidos de la base class listados después del especificador de acceso son miembros protegidos de la clase derivada.
6) Herencia privada : los miembros públicos y protegidos de la clase base listados después del especificador de acceso son miembros privados de la clase derivada.

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

1) a los miembros y amigos de esa clase;
2) a los miembros y amigos de cualquier clase derivada de esa clase, pero solo cuando la clase del objeto a través del cual se accede al miembro protegido es esa clase derivada o una clase derivada de esa clase derivada:
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

public , protected , private

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