Suponha-se que se pretendia escrever um programa para gerir o pessoal de uma
empresa.  Podíamos começar pelo conceito mais simples: o de
empregado.  Normalmente cada conceito, que corresponde usualmente a um substantivo
na linguagem corrente, corresponde a uma classe.  Vamos pois começar por
definir a classe Empregado. Suponha-se, para simplificar, que a
informação relevante acerca de cada empregado é o seu nome e sexo:
class Empregado {public:Empregado(string const& nome, Sexo const sexo);
string const& nome() const;Sexo sexo() const;void mostra() const;
private:string nome_;Sexo sexo_;};
inline Empregado::Empregado(string const& nome, Sexo const sexo): nome_(nome), sexo_(sexo)
{}
inline string const& Empregado:: nome() const
{return nome_;}
inline Sexo Empregado:: sexo() const
{return sexo_;}
inline void Empregado:: mostra() const
{cout << "Nome: " << nome() << endl<< "Sexo: " << sexo() << endl;}
Notar que se admite que Sexo está definida algures.
Isto está muito bonito e trivial, mas na prática há vários tipos de empregados. Os chefes, por exemplo, têm um nível de chefia e um secretário atribuído, os motoristas têm um veículo a seu cargo, cada secretário pode secretariar vários chefes...
Pensemos primeiro nos chefes. Os chefes têm tudo o que um empregado tem, mas adicionalmente têm um nível de chefia, para simplificar. Solução: escrever uma nova classe:
class Chefe {public:Chefe(string const& nome, Sexo const sexo, int const nível);
string const& nome() const;Sexo sexo() const;int nível() const;void mostra() const;
private:string nome_;Sexo sexo_;int nível_;};
inline Chefe::Chefe(string const& nome, Sexo const sexo, int const nível): nome_(nome), sexo_(sexo), nível_(nível)
{}
inline string const& Chefe:: nome() const
{return nome_;}
inline Sexo Chefe:: sexo() const
{return sexo_;}
inline int Chefe::nível() const
{return nível_;}
inline void Chefe:: mostra() const
{cout << "Nome: " << nome() << endl<< "Sexo: " << sexo() << endl<< "Nível: " << nível() << endl;}
Esta solução tem vários problemas.
Repito: um chefe é um empregado. Esta é uma relação muito importante e que se presta a confusões. O que queremos dizer é que qualquer chefe é também um empregado.
É muito diferente dizer "eu sou um humano" ou dizer "um humano é um mamífero".
Escrever no quadro.
A primeira relação é entre uma instância ou objecto e a sua classe: eu sou um objecto da classe dos humanos.
A segunda é uma relação entre duas classes: os humanos são mamíferos.
A primeira é uma relação de instanciação: eu sou um exemplo ou instância ou exemplar dos humanos.
A segunda é uma relação de especialização ou generalização, consoante a direcção em que olharmos. O conceito de humano é uma especialização de mamífero e o conceito de mamífero é uma generalização de humano (e não só...).
É interessante fazer um paralelo entre a linguagem natural e o C++. O quadro que se segue tem de se ir construindo ao longo da aula:
| Linguagem natural | C++ | 
|---|---|
| Nome comum: "humano" | Classe: 
 Definida como: 
 
 
 | 
| Nome próprio: "Zé" | Variável: 
 | 
| "O Zé é um humano." | Definição de variável: 
 | 
| "Um humano é um mamífero", ou "Qualquer humano é um mamífero". | Derivação de classe: 
 
 
 | 
| "Os empregados têm (estão associados a) um chefe." | Associação simples: 
 
 
 | 
| "Uma turma tem (agrega) alunos." | Agregação: 
 
 
 | 
| "Um humano tem (é composto por) cabeça." | Composição: 
 
 
 | 
Queremos pois representar a relação de generalização que existe entre chefe e empregado. Essa relação pode ser representada como se segue:

Que impacto deveria ter no nosso código que o C++ soubesse que um Chefe
é um  Empregado?  Várias.  A primeira e
menos útil seria que nos permitiria escrever algo como:
Chefe ana_maria("Ana Maria", feminino, 4);
Empregado sósia_de_ana_maria_como_empregado = ana_maria;
A esta operação, se fosse possível, chamar-se-ia corte (slicing), pois faz uma cópia da Ana Maria cortando tudo aquilo que a torna chefe (o que será?). É uma operação perigosa (dolorosa?) e de evitar, como se verá.
Mais importante era que nos fosse possível colocar ponteiros para todos os empregados numa lista, independentemente do seu tipo específico, e percorrer essa lista para, por exemplo, visualizar todos os empregados:
list<Empregado*> empregados;
empregados.push_back(new Empregado("João Maria", masculino));
empregados.push_back(new Chefe("Ana Maria", feminino, 4));...
for(list<Empregado*>::const_iterator i = empregados.begin();
i != empregados.end();
++i) {
(*i)->mostra();
cout << endl;
}
E que surgisse:
Nome: João Maria
Sexo: masculino
Nome: Ana Maria
Sexo: feminino
Nível: 4
Neste ponto fazer representação UML da lista e explicar diferença entre tipo do ponteiro e tipo do objecto apontado.

Discutir com os alunos possível soluções para o problema. Tentar chegar à solução "pedestre" de uma só classe para depois a discutir. Se se chegar à solução de um empregado dentro de um chefe dizer que já se discute.
Uma possível tentativa de resolver o problema passa por definir apenas uma classe e distinguir internamente o tipo específico de empregado através, por exemplo, de um tipo enumerado:
class Empregado {
public:
enum Tipo {empregado, chefe};
Empregado(Tipo tipo,string const& nome, Sexo const sexo, int const nível = 0);
string const& nome() const;
Sexo sexo() const;
int nível() const;
void mostra() const;
private:
string nome_;
Sexo sexo_;
int nível_;
Tipo tipo;
};
inline Empregado::Empregado(Tipo tipo,string const& nome, Sexo const sexo,
int const nível = 0)
: tipo(tipo), nome_(nome), sexo_(sexo), nível_(nivel)
{
assert(tipo != chefe or nível == 0);
}
inline string const& Empregado:: nome() const
{
return nome_;
}
inline Sexo Empregado:: sexo() const
{
return sexo_;
}
inline int Empregado:: nível() const
{
assert(tipo == chefe);
return nível_;
}
inline void Empregado:: mostra() const
{
cout << "Nome: " << nome() << endl
<< "Sexo: " << sexo() << endl;
if(tipo == chefe)
cout << "Nível: " << nível() << endl;
}
O primeiro problema que surge é que nível atribuir aos empregados. Aqui arbitrou-se ser nível zero, mas nem sempre poderá ser possível uma solução semelhante.
Imagine-se agora que se pretendia representar também secretários,
motoristas, chefes dos motoristas, carpinteiros, etc...  Em breve o caos
estava instalado.  O construtor teria de prever todos os casos e verificar
todas as combinações impossíveis (e.g., atribuição de automóveis a
carpinteiros, por exemplo).  Todos os métodos com especializações para cada tipo de empregado seriam monstruosos,
provavelmente dominados por inúmeros e gigantescos switch... 
Para além da questão estética, não despicienda, este código era: 
Há uma outra possível solução. Usam-se duas classes separadas mas põe-se um empregado dentro de um chefe:
class Chefe {public:Chefe(string const& nome, Sexo const sexo, int const nível);
string const& nome() const;Sexo sexo() const;int nível() const;void mostra() const;
private:Empregado empregado;int nível_;};
inline Chefe::Chefe(string const& nome, Sexo const sexo, int const nível): empregado(nome, sexo), nível_(nível)
{}
inline string const&Chefe:: nome() const
{return empregado.nome();}
inline SexoChefe::sexo() const
{
return empregado.sexo();}
inline intChefe::nível() const
{return nível_;}
inline voidChefe::mostra() const
{empregado.mostra();cout << "Nível: " << nível() << endl;}
Esta solução tem pelo menos a vantagem de poupar código.  Reparem
como o Chefe delega no Empregado a maior parte das
suas tarefas...  O problema é que ainda não é possível tratar um chefe
como se de um empregado se tratasse e, pior, dá a sensação que os chefes
possuem fisicamente um pequenino homúnculo dentro deles encarregue de trabalhar por
eles...  O nosso código não corresponde ao que queremos modelar, o que
é sempre má ideia.
Podíamos resolver parcialmente o primeiro problema acrescentando na classe Empregado
um construtor que recebesse um chefe como argumento e dele utilizasse apenas o
que é relevante para construir um empregado (cortando a informação
especializada) e um operador de atribuição semelhante.  Mas isso
implicaria que, por cada conceito especializado de empregado seria necessário
um construtor e um operador de atribuição adicionais na classe Empregado.  E, além disso,
nada disto nos permitiria escrever o nosso código objectivo:
list<Empregado*> empregados;
empregados.push_back(new Empregado("João Maria", masculino));
empregados.push_back(new Chefe("Ana Maria", feminino, 4));...
for(list<Empregado*>const_iterator i = empregados.begin();
i != empregados.end();
++i) {
(*i)->mostra();
cout << endl;
}
É importante perceber-se bem o interesse das instruções:
Chefe o_zé("Zé Maria", masculino, 4);
Empregado& de_novo_o_zé = o_zé;
Empregado* ponteiro_para_o_zé = &zé;
Desenhar diagrama UML!
É que nos permite, por exemplo, colocar endereços de qualquer tipo específico de empregado numa lista e percorrê-la duma forma uniforme, como vimos. Ou permite-nos escrever um procedimento:
void pagaSalário()...
{
}
capaz de lidar com qualquer tipo específico de empregado.
A solução para este dilema é-nos fornecida pelo chamado mecanismo de herança do C++.
A sintaxe é a seguinte:
class Chefe : public Empregado {...
};
Dir-se-á que a classe Chefe  herda de, deriva de ou especializa a
classe Empregado.  Logo, um Chefe é um
Empregado.  Empregado é a classe base 
nesta relação e Chefe a classe derivada.
Dizer que podia haver mais do que uma base!
Em bom rigor herdar e especializar não são necessariamente a mesma coisa, mas o C++ confunde os dois conceitos. Quem quiser discutir em privado está à vontade....
O que acontece é que a classe Chefe herda da classe Empregado
todos os seus atributos e operações.  Assim, só é necessário definir o
que é novo ou especializado.  Ou seja:
class Chefe : public Empregado {public:Chefe(string const& nome, string const& morada,
Sexo const sexo, int const nível);
int nível() const;void mostra() const;
private:int nível_;};
inline int Chefe::nível() const
{return nível_;}
Discutir:
mostra().  Que significa?  Discutir
    ocultação!Perfeito.  A classe derivada herdou tudo o que queria da classe base,
acrescentou o inspector para o nível e tem de especializar o método mostra().  Para isso fornece uma versão ele próprio, ocultando e
especializando a versão da classe base: 
inline void Chefe::mostra() const
{Empregado::mostra();cout << "Nível: " << nível() << endl;}
Explicar necessidade do operador de resolução de âmbito.
E o construtor?
inline Chefe::Chefe(string const& nome, string const& morada,
Sexo const sexo, int const nível)
: ?????
{
}
Acontece que é necessário inicializar atributos privados da classe base. Não podemos escrever:
inline Chefe::Chefe(string const& nome, string const& morada,
Sexo const sexo, int const nível)
: nome_(nome), sexo_(sexo), nível_(nível)
{
}
Nem seria boa ideia! Discutir brevemente invariantes de classe.
O C++ diz-nos que devemos inicializar a parte do novo objecto que é da classe base invocando o seu construtor na lista de inicializadores:
inline Chefe::Chefe(string const& nome, string const& morada,
Sexo const sexo, int const nível)
: Empregado(nome, sexo), nível_(nível)
{
}
Explicar ordem de construção:
A destruição ocorre pela ordem inversa!
É possível definir hierarquias de classes. Por exemplo:

Mas ainda não resolvemos o problema! Se corrermos o nosso querido pedaço de programa:
list<Empregado*> empregados;...
empregados.push_back(new Empregado("João Maria", masculino));
empregados.push_back(new Chefe("Ana Maria", feminino, 4));
for(list<Empregado*>const_iterator i = empregados.begin();
i != empregados.end();
++i) {
(*i)->mostra();
cout << endl;
}
O que aparece?
Discutir com base no diagrama UML:

Concluir que é:
Nome: João Maria
Sexo: masculino
Nome: Ana Maria
Sexo: feminino
Ou seja, o método mostra()  executado aquando da
invocação de mostra() depende do ponteiro
através do qual se faz a invocação e não do verdadeiro tipo do objecto
apontado!  
De facto, através da herança estabelecida, o C++ já considera que um Chefe
é um Empregado.  Mas ainda falta algo.
A ligação entre a operação invocada (neste caso mostrar()) e
o método efectivamente executado é neste caso estática (decidida pelo
compilador).  Falta-nos polimorfismo, que permite a operações realizadas
sobre objectos apontados por ponteiros de um único tipo terem comportamentos diversos
consoante o objecto apontado: falta-nos ligação dinâmica entre a
operação invocada e o método executado (decidida durante a execução do
programa).  Assunto para a próxima aula...
Finalmente, para terminar a aula, uma pequena explicação.  Que
acontece se, ao especificarmos uma herança, usarmos a palavra chave private? 
Apontar exemplo no quadro.  Duas coisas:
Para que serve então? Para nada, praticamente. Bom, tem algumas aplicações restritas. Sempre que um conceito for diferente de outro, mas puder ser implementado à sua custa com pequenas variações, que correspondem normalmente a uma restrição da interface, então pode-se usar. No fundo, uma herança privada estabelece um tipo especial de relação:
funciona como um ... mas
Pense-se no caso das pilhas e das listas. Qual pode ser implementada à custa de qual?
Discutir!
É claro que:
a classe
PinhaDeIntfunciona como umaListaDeInt, mas apenas permite algumas operações, com nomes diferentes!
Claro está que se podia implementar a classe PilhaDeInt à custa
de um atributo da classe ListaDeInt:
class PilhaDeInt {public:typedef ListaDeInt::Item Item;
int altura() const;bool estáVazia() const;
Item const& topo() const;
Item& topo();
void põe(Item const& novo_item);void tiraItem();
private:ListaDeInt lista;};
inline int PilhaDeInt:: altura() const
{return lista.comprimento();}
inlineboolPilhaDeInt::estáVazia() const
{return lista.estáVazia();}
inlineItem const&PilhaDeInt::topo() const
{return lista.trás();}
inlineItem&PilhaDeInt::topo()
{return lista.trás();}
inlinevoidPilhaDeInt::põe(Item const& novo_item)
{lista.põeAtrás(novo_item);}
inlinevoidPilhaDeInt::tiraItem()
{lista.tiraDeTrás();}
Mas é mais simples fazer uma herança privada e alterar só o que é necessário:
class PilhaDeInt : private ListaDeInt {public:typedef ListaDeInt::Item Item;
int altura() const;bool estáVazia() const;
Item const& topo() const;
Item& topo();
void põe(Item const& novo_item);void tiraItem();};
inline int PilhaDeInt::altura() const
{return comprimento();}
inlineboolPilhaDeInt::estáVazia() const
{return ListaInt::estáVazia();}
inlineItem const&PilhaDeInt::topo() const
{return trás();}
inlineItem&PilhaDeInt::topo()
{return trás();}
inlinevoidPilhaDeInt::põe(Item const& novo_item)
{põeAtrás(novo_item);}
inlinevoidPilhaDeInt::tiraItem()
{tiraDeTrás();}
Ou seja, a herança privada é apenas uma forma estranha de composição. Mas como a composição original também estava a ser usada de uma forma estranha, pois não se pode dizer que uma pilha seja composta por uma lista....
Simplificam-se as coisas se se disser que determinados membros da classe base devem ser públicos da classe derivada apesar da herança ser privada:
class PilhaDeInt : private ListaDeInt {public:using ListaDeInt::Item;
int altura() const;
using ListaInt::estáVazia;
Item const& topo() const;
Item& topo();
void põe(Item item);
void tiraItem();};
inline int PilhaDeInt::altura() const
{return comprimento();}
inlineItem const&PilhaDeInt::topo() const
{return trás();}
inlineItem&PilhaDeInt::topo()
{return trás();}
inlinevoidPilhaDeInt::põe(Item const& novo_item)
{põeAtrás(novo_item);}
inlinevoidPilhaDeInt::tiraItem()
{tiraDeTrás();}
Se estivermos dispostos a usar os mesmos nomes já existentes...
class PilhaDeInt : private ListaDeInt {public:using ListaDeInt::Item;
using ListaDeInt::comprimento;using ListaDeInt::estáVazia;
using ListaDeInt::trás;
using ListaDeInt::põeAtrás;using ListaDeInt::tiraDeTrás;};