Publicado em Programação

Tipos de herança

Uma classe pode conter até três secções:

tiposdeheranca01

  • Pública (public)
  • Protegida (protected)
  • Privada (private)

Do ponto de vista do utilizador da classe, que podemos considerar a título de exemplo a função main( ), apenas os membros da secções pública lhe são acessíveis. É por esta razão que a secção pública também é designada de “interface da classe“.

Os membros privados e protegidos só podem ser acedidos pela própria classe.

Os membros protegidos poderão ser acedidos pelas classes derivadas, dependendo do tipo de herança estabelecido entre estas.

Quando abordei aqui este assunto pela primeira vez ignorei propositadamente a secção protegida, e o tipo de esquema utilizado na altura foi o seguinte.

class2

É como se agora este núcleo fosse separado em duas secções distintas. A privada e a protegida.

Um exemplo …

#include <iostream>
using namespace std;

class Base{
public:
	int x;
protected:
	int y;
private:
	int z;
};

int main(){
	Base A;
	A.x = 1;	//Correcto
	//A.y = 2;	//Erro - y é membro protegido
	//A.z = 3;	//Erro - x é membro privado
}

Note que as lihnas 16 e 17, se descomentadas, produziriam os seguintes erros:

main.cpp: In function ‘int main()’:
main.cpp:8: error: ‘int Base::y’ is protected
main.cpp:16: error: within this context
main.cpp:10: error: ‘int Base::z’ is private
main.cpp:17: error: within this context

Assim sendo, vamos agora enumerar os tipos de herança que podemos definir entre classes.

Herança pública

tiposdeheranca021

Um exemplo fictício …

#include <iostream>
using namespace std;

class Base{
public:
	int x;
protected:
	int y;
private:
	int z;
};

class Derivada1:public Base{
public:
	Derivada1(){
		w = 33;
	}
	void f1(){
		x = 0;	// A classe Derivada tem acesso aos membros públicos da Base
		y = 0;	// A classe Derivada tem acesso aos membros protegidos da Base
		//z = 0;// A classe Derivada não tem acesso aos membros privados da Base
	}
protected:
	void f2(){
		w = 2 * x;
	}
private:
	int w;
};

int main(){
	Derivada1 B;
	//Testar o acesso à classe derivada
	B.f1();		//Correcto
	//B.f2();	//Erro - f1() é membro protegido
	//B.w = 5;	//Erro - f1() é membro privado

	//Testar o acesso aos membros da classe base
	B.x = 7;	//Correcto
	//B.y = 6;	//Erro - y é membro protegido da classe base
	//B.z = 5;	//Erro - z é membro privado da classe base
}
  • Este é o tipo de herança utilizado na maioria das vezes.
  • Tal como representado na imagem, o utilizador continua a ter acesso às interfaces das duas classes, ou seja, aos membros das suas secções públicas (ver linhas 34 e 39). Todos os membros públicos da classe Base  são considerados membros públicos da classe Derivada.
  • Cada classe continua a ter acesso às suas secções pública, protegida e privada, como aliás seria de esperar.
  • A classe derivada ganha acesso à secção protegida da classe base (ver linha 20). Herda os seus membros protegidos e mantém-nos como tal.
  • Os membros públicos são herdados como públicos.
  • Os membros protegidos são herdados como protegidos.

Recomendo compilar o exemplo descomentando as linhas comentadas. As mensagens de erro dizem tudo!

Herança protegida

tiposdeheranca03

Se utilizarmos herança protegida teremos:

  • Os membros públicos e protegidos passam a ser membros protegidos da classe derivada, ou seja, o utilizador deixa de ter acesso à interface da classe base.
  • No exemplo anterior, alterando o tipo de herança na linha 13 de public para protected, passamos a obter um erro.
  • Para todos os efeitos o membros públicos e protegidos da classe basse passam agora a ser considerados membros protegidos da classe derivada, e portanto acessíveis apenas pelas classes que voltarem a herdar a classe derivada.

Herança privada

tiposdeheranca04

Relativamente à herança privada:

  • Na herança privada, o utilizador da classe Derivada não tem acesso a nenhum dos membros da classe Base, o que aliás já acontecia na situação anterior.
  • Estes tornam-se todos privados, pelo que o utilizador da classe derivada deixa de poder utilizar as funcionalidades da classe Base.
  • Esta fica  fica totalmente oculta ao utilizador. Deste facto resulta a possibilidade se se proceder a alterações na classe Base sem necessidade alteração à interface proporcionada ao utilizadorm pela classe Derivada.
  • Caso a classe Derivada venha a ser herdada por outra classe, todos os membros da classe Base ficam com acesso restrido à sua super-classe, e não aos outros níveis da hierarquia de herança.

Considero este assunto simultaneamente simples e complexo.

Simples porque as regras são claras quando analisadas separadamente.

Complexo porque se imaginarmos um hierarquia de classes com alguma complexidade pode tornar-se complicada a sua interpretação.

De qualquer das formas espero ter conseguido com estes esquemas e pequenos exemplos fazer passar as ideias mais importantes.

Para terminar, deixo o quadro resumo.

cartazheranca

Bom proveito e qualquer chamada de atenção é bem vinda!

Anúncios

7 opiniões sobre “Tipos de herança

  1. teoricamente é fácil de entender o conceito, mas por acaso você teria exemplos reais de quando uma herança privada seria interessante?

    Veja que em Java não existe estes conceitos, então será que isso é realmente necessário conhecer?

    Talvez um exemplo esclareça a dúvida.

  2. Fred and Wilma, para quem gostar dos Flinstones:
    http://www.parashift.com/c++-faq-lite/private-inheritance.html

    “Use composition when you can, private inheritance when you have to.

    Normally you don’t want to have access to the internals of too many other classes, and private inheritance gives you some of this extra power (and responsibility). But private inheritance isn’t evil; it’s just more expensive to maintain, since it increases the probability that someone will change something that will break your code.

    A legitimate, long-term use for private inheritance is when you want to build a class Fred that uses code in a class Wilma, and the code from class Wilma needs to invoke member functions from your new class, Fred. In this case, Fred calls non-virtuals in Wilma, and Wilma calls (usually pure virtuals) in itself, which are overridden by Fred. This would be much harder to do with composition.

    class Wilma {
    protected:
    void fredCallsWilma()
    {
    std::cout << “Wilma::fredCallsWilma()\n”;
    wilmaCallsFred();
    }
    virtual void wilmaCallsFred() = 0; // A pure virtual function
    };

    class Fred : private Wilma {
    public:
    void barney()
    {
    std::cout << “Fred::barney()\n”;
    Wilma::fredCallsWilma();
    }
    protected:
    virtual void wilmaCallsFred()
    {
    std::cout << “Fred::wilmaCallsFred()\n”;
    }
    };

  3. Ola Graciano, obrigado pelos esclarecimentos.

    Gostei muito da explicação do resumo a seguir:

    Private inheritance models the “is implemented in terms of a” relationship, rather than the is-a relationship that is modeled by public inheritance.

    (Do linke que você passou: http://www.cprogramming.com/tutorial/private.html)

    Em Java que não possui definição escopo de herança, pois acredito que isso é mais interessante no contexto de linguagens com suporte a herança múltipla. Em Java o comportamento “é implementado em termos de” é implementado com uma composição de classes.

    O exemplo dos Flinstones ilustra a dupla possibilidade de implementação: “Use composition when you can, private inheritance when you have to.”

    Apesar de que eu ainda não tenha visto um “have to” forte o suficiente que não possa ser feito com composição, ou que torne isso demasiadamente complicado.

    Mais uma vez, obrigado pelos esclarecimentos.

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s