Controle de acesso a membro (C++)

Os controles de acesso permitem separar a interface public de uma classe dos detalhes da implementação private e dos membros protected que são usados apenas por classes derivadas. O especificador de acesso se aplica a todos os membros declarados depois dele até que o próximo especificador de acesso seja encontrado.

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();
};

O acesso padrão é private em uma classe e public em um struct ou união. Os especificadores de acesso em uma classe podem ser usados inúmeras vezes em qualquer ordem. A alocação de armazenamento para objetos de tipos de classe depende da implementação. No entanto, os compiladores devem garantir a atribuição de membros a endereços de memória sucessivamente mais altos entre especificadores de acesso.

Controle de acesso de membros

Tipo de acesso Significado
private Os membros de classe declarados como private podem ser usados apenas pelas funções de membro e amigos (classes ou funções) da classe.
protected Os membros de classe declarados como protected podem ser usados pelas funções de membro e por amigos (classes ou funções) da classe. Além disso, eles podem ser usados por classes derivadas da classe.
public Os membros de classe declarados como public podem ser usados por qualquer função.

O controle de acesso ajuda a evitar que você use objetos de maneiras que não sejam as pretendidas. Essa proteção é perdida quando você faz conversões de tipo explícitas (casts).

Observação

O controle de acesso é igualmente aplicável a todos os nomes: funções membro, dados de membro, classes aninhadas e enumeradores.

Controle de acesso em classes derivadas

Dois fatores controlam quais membros de uma classe base são acessíveis em uma classe derivada; esses mesmos fatores controlam o acesso aos membros herdados na classe derivada:

  • Se a classe derivada declara a classe base usando o especificador de acesso public.

  • Qual é o acesso ao membro na classe base.

A tabela a seguir mostra a interação entre esses fatores e como determinar o acesso do membro da classe base.

Acesso do membro na classe base

private protected public
Sempre inacessível com qualquer acesso de derivação private na classe derivada se você usar a derivação private private na classe derivada se você usar a derivação private
protected na classe derivada se você usar a derivação protected protected na classe derivada se você usar a derivação protected
protected na classe derivada se você usar a derivação public public na classe derivada se você usar a derivação public

O exemplo a seguir ilustra a derivação de acesso:

// 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
}

Em DerivedClass1, a função membro PublicFunc é um membro public; e ProtectedFunc é um membro protected protegido porque BaseClass é uma classe base public. PrivateFunc é private para BaseClass, sendo inacessível a quaisquer classes derivadas.

Em DerivedClass2, as funções PublicFunc e ProtectedFunc são consideradas membros private porque BaseClass é uma classe base private. Novamente, PrivateFunc é private para BaseClass, sendo inacessível a quaisquer classes derivadas.

Você pode declarar uma classe derivada sem um especificador de acesso de classe base. Nesse caso, a derivação será considerada private se a declaração de classe derivada usar a palavra-chave class. A derivação será considerada public se a declaração de classe derivada usar a palavra-chave struct. Por exemplo, o código a seguir:

class Derived : Base
...

é equivalente a:

class Derived : private Base
...

Da mesma forma, o código a seguir:

struct Derived : Base
...

é equivalente a:

struct Derived : public Base
...

Os membros declarados como tendo acesso private não são acessíveis a funções ou classes derivadas, a menos que essas funções ou classes sejam declaradas usando a declaração friend na classe base.

Um tipo union não pode ter uma classe base.

Observação

Ao especificar uma classe base private, recomendamos usar explicitamente a palavra-chave private para que os usuários da classe derivada reconheçam o acesso do membro.

Controle de acesso e membros estáticos

Quando você especifica uma classe base como private, isso afeta apenas os membros não estáticos. Os membros estáticos públicos ainda são acessíveis nas classes derivadas. No entanto, acessar membros da classe base usando ponteiros, referências ou objetos pode exigir uma conversão, o que aplica o controle de acesso novamente. Considere o seguinte exemplo:

// 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;
}

No código acima, o controle de acesso proíbe a conversão de um ponteiro para Derived2 em um ponteiro para Base. O ponteiro this é implicitamente do tipo Derived2 *. Para selecionar a função CountOf, this deve ser convertido no tipo Base *. Essa conversão não é permitida porque Base é uma classe base indireta private para Derived2. A conversão em um tipo de classe base private é aceitável apenas em ponteiros para as classes derivadas imediatas. É por isso que os ponteiros do tipo Derived1 * podem ser convertidos no tipo Base *.

Observe que uma chamada explícita para a função CountOf, sem usar um ponteiro, uma referência ou um objeto para selecioná-la, não implica em conversão. É por isso que a chamada é permitida.

Os membros e amigos de uma classe derivada, T, podem converter um ponteiro para T em um ponteiro para uma classe base direta private de T.

Acesso a funções virtuais

O controle de acesso aplicado a funções virtual é determinado pelo tipo usado para fazer a chamada de função. Substituir as declarações da função não afeta o controle de acesso para um determinado tipo. Por exemplo:

// 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;
}

No exemplo anterior, chamar a função virtual GetState usando um ponteiro para o tipo VFuncBase chama VFuncDerived::GetState, e GetState é tratada como public. No entanto, chamar GetState usando um ponteiro para o tipo VFuncDerived é uma violação de controle de acesso porque GetState é declarada como private na classe VFuncDerived.

Cuidado

A função virtual GetState pode ser chamada usando um ponteiro para a classe base VFuncBase. Isso não significa que a função chamada seja a versão da classe base dessa função.

Controle de acesso com herança múltipla

Em células entrelaçadas de herança múltipla que envolvem classes base virtuais, um nome determinado pode ser acessado por mais de um caminho. Como o controle de acesso diferente pode ser aplicado ao longo desses caminhos diferentes, o compilador escolhe o caminho que fornece o maior acesso. Veja a figura a seguir:

Diagram showing access along the paths of an inheritance graph.

O diagrama mostra a seguinte hierarquia de herança: classe VBase é a classe base. Classe LeftPath herda do VBase usando VBase virtual private . classe RightPath também herda do VBase, mas usando VBase virtual public . Finalmente, a classe Derived herda da classe LeftPath e da classe RightPath usando public LeftPath, public RightPath.

Acesso com caminhos de um gráfico de herança

Na figura, um nome declarado na classe VBase é sempre acessado pela classe RightPath. O caminho à direita é mais acessível porque RightPath declara VBase como uma classe base public, enquanto que LeftPath declara VBase como private.

Confira também

Referência da linguagem C++