Control de acceso a miembros (C++)

Los controles de acceso permiten separar la public interfaz de una clase de los private detalles de implementación y de los protected miembros que solo pueden usar las clases derivadas. El especificador de acceso se aplica a todos los miembros declarados después de él hasta que se encuentra el especificador de acceso siguiente.

class Point
{
public:
    Point( int, int ) // Declare public constructor.;
    Point();// Declare public default constructor.
    int &x( int ); // Declare public accessor.
    int &y( int ); // Declare public accessor.

private:                 // Declare private state variables.
    int _x;
    int _y;

protected:      // Declare protected function for derived classes only.
    Point ToWindowCoords();
};

El acceso predeterminado es private en una clase, y public en una estructura o unión. Los especificadores de acceso de una clase se pueden usar cualquier número de veces en cualquier orden. La asignación de almacenamiento para los objetos de los tipos de clase depende de la implementación. Sin embargo, los compiladores deben garantizar la asignación de miembros a direcciones de memoria sucesivamente superiores entre los especificadores de acceso.

Control de acceso a miembros

Tipo de Acceso Significado
private Los miembros de la clase declarados como private solo pueden ser usados por las funciones miembro y amigos (clases o funciones) de la clase.
protected Los miembros de la clase declarados como protected pueden ser usados por las funciones miembro y amigos (clases o funciones) de la clase. Además, las clases derivadas de la clase también pueden usarlos.
public Los miembros de la clase declarados como public pueden ser usados por cualquier función.

El control de acceso ayuda a evitar que se utilicen los objetos de forma no prevista. Esta protección se pierde cuando se realizan conversiones de tipo explícitas (conversiones).

Nota:

El control de acceso también es aplicable a todos los nombres: funciones miembro, datos de miembro, clases anidadas y enumeradores.

Control de acceso en clases derivadas

Dos factores controlan los miembros de una clase base que están accesibles en una clase derivada; estos mismos factores controlan el acceso a los miembros heredados en la clase derivada:

  • Si la clase derivada declara la clase base usando el public especificador de acceso.

  • Que el acceso al miembro esté en la clase base.

En la tabla siguiente se muestra la interacción entre estos factores y cómo se determina el acceso a miembros de clase base.

Acceso a miembros de clase base

private protected public
Siempre inaccesible con cualquier acceso de derivación private en la clase derivada si se utiliza private derivación private en la clase derivada si se utiliza private derivación
protected en la clase derivada si se utiliza protected derivación protected en la clase derivada si se utiliza protected derivación
protected en la clase derivada si se utiliza public derivación public en la clase derivada si se utiliza public derivación

El siguiente ejemplo ilustra la derivación del acceso:

// access_specifiers_for_base_classes.cpp
class BaseClass
{
public:
    int PublicFunc(); // Declare a public member.
protected:
    int ProtectedFunc(); // Declare a protected member.
private:
    int PrivateFunc(); // Declare a private member.
};

// Declare two classes derived from BaseClass.
class DerivedClass1 : public BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

class DerivedClass2 : private BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

int main()
{
    DerivedClass1 derived_class1;
    DerivedClass2 derived_class2;
    derived_class1.PublicFunc();
    derived_class2.PublicFunc(); // function is inaccessible
}

En DerivedClass1, la función miembro PublicFunc es un public miembro y ProtectedFunc es una protected miembro porque BaseClass es una public clase base. PrivateFunc es private a BaseClass, y es inaccesible para cualquier clase derivada.

En DerivedClass2, las funciones PublicFunc y ProtectedFunc se consideran private miembros porque BaseClass es una private clase base. De nuevo, PrivateFunc es private a BaseClass, y es inaccesible para cualquier clase derivada.

Puede declarar una clase derivada sin un especificador de acceso de clase base. En este caso, la derivación se considera private si la declaración de la clase derivada utiliza la class palabra clave. La derivación se considera public si la declaración de la clase derivada utiliza la struct palabra clave. Por ejemplo, el código siguiente:

class Derived : Base
...

equivale a:

class Derived : private Base
...

De igual forma, el código siguiente:

struct Derived : Base
...

equivale a:

struct Derived : public Base
...

Los miembros declarados con private acceso no son accesibles a las funciones o clases derivadas a menos que dichas funciones o clases se declaren usando la friend declaración de la clase base.

Un tipo union no puede tener una clase base.

Nota:

Cuando se especifica una private clase base, es aconsejable utilizar explícitamente laprivate palabra clave para que los usuarios de la clase derivada reconozcan el acceso de miembros.

Control de acceso y miembros estáticos

Cuando se especifica una clase base como private, solo afecta a los miembros no estáticos. Los miembros estáticos públicos siguen siendo accesibles en las clases derivadas. Sin embargo, el acceso a los miembros de la clase base mediante punteros, referencias u objetos puede requerir una conversión, que aplica de nuevo el control de acceso. Considere el ejemplo siguiente:

// access_control.cpp
class Base
{
public:
    int Print();             // Nonstatic member.
    static int CountOf();    // Static member.
};

// Derived1 declares Base as a private base class.
class Derived1 : private Base
{
};

// Derived2 declares Derived1 as a public base class.
class Derived2 : public Derived1
{
    int ShowCount();    // Nonstatic member.
};

// Define ShowCount function for Derived2.
int Derived2::ShowCount()
{
    // Call static member function CountOf explicitly.
    int cCount = ::Base::CountOf();     // OK.

    // Call static member function CountOf using pointer.
    cCount = this->CountOf();  // C2247: 'Base::CountOf'
                               // not accessible because
                               // 'Derived1' uses 'private'
                               // to inherit from 'Base'
    return cCount;
}

En el código anterior, el control de acceso prohíbe la conversión de un puntero a Derived2 en un puntero a Base. El puntero this es implícitamente de tipo Derived2 *. Para seleccionar la CountOf función, this debe convertirse en tipo Base *. Esta conversión no está permitida porque Base es una private clase base indirecta a Derived2. La conversión a un private tipo de clase base solo es aceptable para los punteros a clases derivadas inmediatas. Por ello los punteros de tipo Derived1 * pueden ser convertidos a tipo Base *.

Una llamada explícita a la función CountOf, sin utilizar un puntero, referencia u objeto para seleccionarla, no implica ninguna conversión. Por eso se permite la llamada.

Los miembros y amigos de una clase derivada, T, pueden convertir un puntero a T en un puntero a una private clase base directa de T.

Acceso a funciones virtuales

El control de acceso aplicado a las funciones virtual viene determinado por el tipo utilizado para realizar la llamada a la función. Las declaraciones de anulación de la función no afectan al control de acceso para un tipo determinado. Por ejemplo:

// access_to_virtual_functions.cpp
class VFuncBase
{
public:
    virtual int GetState() { return _state; }
protected:
    int _state;
};

class VFuncDerived : public VFuncBase
{
private:
    int GetState() { return _state; }
};

int main()
{
   VFuncDerived vfd;             // Object of derived type.
   VFuncBase *pvfb = &vfd;       // Pointer to base type.
   VFuncDerived *pvfd = &vfd;    // Pointer to derived type.
   int State;

   State = pvfb->GetState();     // GetState is public.
   State = pvfd->GetState();     // C2248 error expected; GetState is private;
}

En el ejemplo anterior, la llamada a la función virtual GetState utilizando un puntero al tipo VFuncBase llama VFuncDerived::GetState, y GetState se trata como public. Sin embargo, llamar GetState usando un puntero al tipo VFuncDerived es una violación de control de acceso porque GetState se declara private en la clase VFuncDerived.

Precaución

La función virtual GetState se puede llamar mediante un puntero a la clase base VFuncBase. Esto no significa que la función llamada sea la versión de la clase base de esa función.

Control de acceso con herencia múltiple

En los entramados de herencia múltiple con clases base virtuales, se puede acceder a un nombre determinado a través de más de una ruta. Dado que se puede aplicar un control de acceso diferente en estas rutas de acceso diferentes, el compilador elige la ruta de acceso que proporciona el mejor acceso. Consulte la ilustración siguiente:

Diagram showing access along the paths of an inheritance graph.

En el diagrama se muestra la siguiente jerarquía de herencia: la clase VBase es la clase base. La clase LeftPath hereda de VBase mediante VBase virtual private . La clase RightPath también hereda de VBase, pero usa VBase virtual public . Por último, la clase Derivada hereda de la clase LeftPath y la clase RightPath mediante public LeftPath, public RightPath.

Acceso mediante rutas de acceso de un gráfico de herencia

En la ilustración, se accede a un nombre declarado en la clase VBase siempre a través de la clase RightPath. El camino correcto es más accesible porque RightPath declara VBase como una public clase base, mientras que LeftPath declara VBase como private.

Consulte también

Referencia del lenguaje C++