Desenvolvimento - C/C++

Proteção dos membros protected

Quando queremos que um membro de nossa classe seja visível apenas dentro dos métodos da classe e dentro dos métodos das classes derivadas dessa classe usamos o nível de proteção protected.

por Wanderley Caloni Jr



Quando queremos que um membro de nossa classe seja visível apenas dentro dos métodos da classe e dentro dos métodos das classes derivadas dessa classe usamos o nível de proteção protected. Isso, é claro, não quer dizer que uma classe derivada vá ter acesso aos membros protegidos de outra:

#include <iostream>

using namespace std;


class Base
{
protected:
   int m_protected;
};


class Derivated: public Base
{
public:
   int GetProtected();
   int GetAnotherProtected();
};


class AnotherDerivated: public Base
{
};


int Derivated::GetProtected()
{
   return m_protected; // tudo bem. sou um Derivated; tenho meus direitos!
}


int Derivated::GetAnotherProtected()
{
   AnotherDerivated anotherDeriv;
   return anotherDeriv.m_protected; // inacessível. não estamos em AnotherDerivated!
}


int main()
{
   Derivated deriv;
   
   deriv.GetProtected();
   deriv.GetAnotherProtected(); // isso não vai dar certo...
}


Saída:
error C2248: "Base::m_protected": cannot access protected member declared in class "Base"
        see declaration of "Base::m_protected"
        see declaration of "Base"

Esse é o motivo fundamental do porquê não podemos fazer isso:

// another chance
int Derivated::GetAnotherProtected()
{
   Base base; // somos derivados dessa base, não somos?
   return base.m_protected; // mas não é por isso que vamos acessar um membro protegido de boa.
}

Ao acessar membros protegidos, é importante o tipo da expressão que está do lado esquerdo do "." ou da "->". Afinal, o nível de proteção se baseia no escopo, e as classes são um escopo. É por isso que consigo acessar os membros protegidos de um outro objeto de minha classe, mesmo sendo outro objeto:

// dentro da lei
int Derivated::GetAnotherProtected()
{
   Derivated deriv; // meu irmão: typeid(deriv) == typeid(*this).
   return deriv.m_protected; // mesmo tipo do escopo: meus direitos são preservados
}

A definição do escopo é tudo o que o compilador dispõe para saber se acessa ou não acessa um membro. Podemos ter acesso a m_protected enquanto somos do tipo Derivated, mas não quando o mesmo objeto é usado como Base:

// fora-da-lei
int Derivated::GetAnotherProtected()
{
   Base& base = *this; // os genes da minha mãe.
   return base.m_protected; // eu não me acesso! tipo o subconsciente...
}

Essa proteção parece desnecessária - e até mesmo inválida - quando lidamos com o mesmo objeto que acessa. Afinal, somos nós mesmos! Só que o compilador não sabe disso, e ele deve desconfiar de tudo e de todos para evitar esse tipo de ataque:

// espertinho, hein?
int Derivated::GetAnotherProtected()
{
   AnotherDerivated anotherDeriv; // não acesso os protegidos desse aqui...
   Base& base = anotherDeriv; // mas quem sabe se eu acessar os genes da minha mãe?
   return base.m_protected; // nada feito =(. Também, se nem eu mesmo consegui me acessar...
}

Agora a proteção do compilador faz sentido. Parece um detalhe babaca, mas que muitas vezes pode gerar a discórdia e indignação dos programadores para com a linguagem C++, que permite (quase) tudo.

Artigo original: Caloni.com.br

Wanderley Caloni Jr

Wanderley Caloni Jr