Proteção dos membros protected

Caloni, 2007-10-26 computer ccpp blog

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 Deriv : public Base
   {
   public:
   	int GetProtectedFromBase();
   	int GetProtectedFromDeriv2();
   };
   
   class Deriv2 : public Base
   {
   };
   
   int Deriv::GetProtectedFromBase()
   {
   	return m_protected;
   }
   
   int Deriv::GetProtectedFromDeriv2()
   {
   	Deriv2 deriv2;
     // error C2248: 'Base::m_protected' : cannot
     // access protected member
     // declared in class 'Base'
   	return deriv2.m_protected;
   }
   
   int main()
   {
   	Deriv deriv;
   	deriv.GetProtectedFromBase();
   	deriv.GetProtectedFromDeriv2();
   }
   

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

   int Deriv::GetProtectedFromBase()
   {
     Base base;
     return base.m_protected;
   } 

Ao acessar membros protegidos é importante o tipo da expressão que está do lado esquerdo do "." ou "->". 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:

   int Deriv::GetProtectedFromDeriv2()
   {
     Deriv deriv;
     assert(typeid(deriv) == typeid(*this));
     return deriv.m_protected; // OK
   }

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 Deriv, mas não quando o mesmo objeto é usado como Base:

   int Deriv::GetProtectedFromBase()
   {
     Base& base = *this;
     assert(typeid(base) != typeid(*this));
     return base.m_protected; // ERROR
   }

Essa proteção parece desnecessária e até mesmo incoerente 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":

   int Deriv::GetProtectedFromDeriv2()
   {
     Deriv2 deriv2;
     Base& base = deriv2;
     assert(typeid(deriv2) != typeid(*this));
     return base.m_protected; // ERROR
   }

Agora a proteção do compilador faz sentido. Parece um detalhe frívolo, mas já vi programadores de respeito se debatendo pela "burrice" do compilador. Imaginei que talvez houvesse mais pessoas com a mesma dúvida de se existe ou não um "bug na linguagem".

[alterando_mensagem_de_erro_no_notepad] [typeid_e_os_perigos_do_nao_polimorfismo]