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 Public 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 siguiente figura:
En el diagrama se muestra la siguiente jerarquía de herencia: la clase VBase es la clase base. La clase LeftPath se hereda de VBase mediante VBase private virtual. La clase RightPath también se hereda de VBase, pero usa VBase public virtual. Por último, la clase Derived se hereda de la clase LeftPath y la clase RightPath mediante LeftPath public, RightPath public.
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
.