| FAQ Lite |
| Herança -
private
e protected |
|
 |
[ 24.1
] Como você expressa herança
private ? |
 |
[ 24.2
] Até que ponto herança private
e composição
são similares? |
 |
[ 24.3
] Qual das duas eu devo preferir: composição ou
herança private ? |
 |
[ 24.4
] Devo fazer conversão de tipo de uma classe derivada private para sua classe base? |
 |
[ 24.5
] Herança protected é comparável à herança private ? |
 |
[ 24.6
] Quais as regras de acesso para herança private e para
herança protected ? |
|
|
|
| [ 24.1 ] Como você
expressa herança private ? |
|
| Quando você usa : private ao invés de : public.
Por exemplo: |
|
class Foo : private Bar {
public:
// ...
};
|
| [ 24.2 ] Até que ponto
herança private e composição são similares? |
|
| Herança private é uma variante sintática de composição (tem-um). Por exemplo, o relacionamento Car tem-um Engine pode
ser expresso usando uma composição: |
|
class Engine {
public:
Engine(int numCylinders);
void start(); // Starts this Engine
};
class Car {
public:
Car() : e_(8) { } // Initializes this Car with 8 cylinders
void start() { e_.start(); } // Start this Car by starting its Engine
private:
Engine e_; // Car has-a Engine
};
|
| O mesmo relacionamento tem-um pode ser expresso
usando herança private: |
|
class Car : private Engine { // Car has-a Engine
public:
Car() : Engine(8) { } // Initializes this Car with 8 cylinders
Engine::start; // Start this Car by starting its Engine
};
|
Há várias similaridades entre essas duas formas de composição:
- Em ambos os casos há exatamente um membro Engine contido
em um Car
- Em nenhum dos dois casos os usuários da classe podem
converter um Car* em um
Engine*
Há também várias diferenças:
- A primeira forma é necessária quando você quer conter
várias Engines por Car
- A segunda forma pode introduzir múltipla herança
desnecessária
- A segunda forma permite que membros de Car convertam
um Car* para um Engine*
- A segunda forma permite acesso aos membros protected: da
classe base
- A segunda forma permite que Car sobrescreva funções virtual de Engine
Repare que herança private é usada normalmente
para se ter acesso aos membros
protected: da classe base, mas é geralmente uma
solução imediatista, um paliativo. |
|
| [ 24.3 ] Qual das duas
eu devo preferir: composição ou herança private ? |
|
| Use composição quando você puder, use herança private quando
você for obrigado. Normalmente você não deveria
querer ter acesso aos componentes internos de muitas outras classes, e herança private lhe
dá um pouco desse poder extra, e responsabilidade extra também. Mas herança private não
é uma solução abominável, é apenas mais cara de se manter, já que aumenta a
probabilidade de que se altere algo que impacte código já existente.
Um uso legítimo, não imediatista, para herança private é
quando você quer construir uma
class Fred que usa código existente na class Wilma,
e o código de class Wilma precisa invocar funções membro de sua nova class Fred.
No caso presente, Fred chama funções não-virtual em Wilma, e Wilma chama
funções próprias (usualmente virtuais puras), que foram
sobrescritas por Fred. Esse talvez seja o caso mais difícil de se implementar com composição. |
|
class Wilma {
protected:
void fredCallsWilma()
{
cout << "Wilma::fredCallsWilma()\n";
wilmaCallsFred();
}
virtual void wilmaCallsFred() = 0; // A pure virtual function
};
class Fred : private Wilma {
public:
void barney()
{
cout << "Fred::barney()\n";
Wilma::fredCallsWilma();
}
protected:
virtual void wilmaCallsFred()
{
cout << "Fred::wilmaCallsFred()\n";
}
};
|
| [ 24.4 ] Posso fazer
conversão de tipo de pointer de uma classe derivada private para
sua classe base? |
|
| Geralmente não. O
relacionamento com a classe base é bem determinado a partir de uma função membro, ou de
uma função friend da classe derivada.
Assim, a conversão simples no sentido ascendente da hieraquia de classes, como PrivatelyDer* para Base* (ou
PrivatelyDr& para Base&) é plenamente
segura. Conversão de tipo de pointer nesse caso não é necessária e não é
recomendada. |
|
| [ 24.5 ] Herança protected é comparável à herança private ? |
|
| Semelhanças: ambas permitem sobrescrever funções virtual da classe base private/protected , sem exigência de
que a classe derivada seja um tipo-de classe base. Diferenças:
herança protected permite que as classes derivadas de classes derivadas acessem todas
as classes envolvidas no relacionamento de herança. Ou seja, as classes derivadas em
segundo, terceiros ou mais níveis podem acessar os detalhes de implementação da classe
base. Isso traz tanto benefícios quanto custos. Benefícios: subclasses de classes
derivadas protected podem explorar o relacionamento com a classe base protected.
Custos: as classes derivadas
protected não podem modificar as características do
relacionamento de herança, sob pena de inviabilizar futuras classes derivadas.
Herança protected usa a sintaxe : protected |
|
class Car : protected Engine {
public:
// ...
};
|
[ 24.6 ] Quais as
regras de acesso para herança
private e para
herança protected ? |
|
| Tome as seguintes classes como exemplos: |
|
class B { /*...*/ };
class D_priv : private B { /*...*/ };
class D_prot : protected B { /*...*/ };
class D_publ : public B { /*...*/ };
class UserClass { B b; /*...*/ };
|
- Nenhuma das subclasses pode acessar nada que
seja private em B.
- Em
D_priv, as partes public e protected de B são private.
- Em
D_prot, as partes public e protected de B são protected.
- Em
D_publ, as partes public de B são public e
as partes protected de B são
protected (D_publ é um tipo-de B).
- class UserClass pode acessar apenas as partes public de B.
Para tornar um membro public de B também public em D_priv ou
em D_prot, declare o nome
do membro com o prefixo B::
Por exemplo, para tornar o membro B::f(int,float) public em D_prot, você deve fazer: |
|
class D_prot : protected B {
public:
B::f; // Note: Not B::f(int,float)
};