friend
)friend
. As declarações de amizade podem ficar em qualquer
lugar da classe, pois as categorias de acesso não as afectam. No entanto,
como normalmente as relações de amizade entre classes ou classes e rotinas
são pormenores de implementação, é recomendável deixar a sua declaração
para a parte privada da classe.
Tal como para as rotinas, também a existência de uma classe pode ser declarada antes de se fazer a sua definição. A declaração de uma classe contém apenas o cabeçalho, não se especificando os seus membros.
Exemplo:
Este tipo de excepção às restrições de acesso (promiscuidades) deve ser utilizado o menos possível e apenas onde não houver melhores alternativas.
//
Declaração da classeAmiga
:
class Amiga;
class Promíscua {
public:
...
private:
int variável_privada;
int função_privada(int);
...
friend class Amiga; //
todos os métodos da classeAmiga
têm acesso
//
a todos os membros da classePromíscua
(inclusive
//
os privados).
friend void penetra(); //
o procedimentopenetra()
tem acesso a todos os
//
membros da classePromíscua
(inclusive os privados).
};
...
class Amiga {
public:
...
void fura(Promíscua& p);
...
};
void Amiga::fura(Promíscua& p)
{
++p.variável_privada; //
acesso a membro privado da classePromíscua
.
cout << p.função_privada(2); //
invocação de operação privada da classePromíscua
.
}
//
Este procedimento é amigo da classePromíscua
!
void penetra()
{
Promíscua p;
++p.variável_privada; //
acesso a um membro privado da classePromíscua
.
cout << p.função_privada(2); //
invocação de operação privada da classePromíscua
.
}
//
Este procedimento não é amigo da classePromíscua
!
void melga()
{
Promíscua p;
++p.variável_privada; //
tentativa de acesso a um membro privado da
//
classePromíscua
.
cout << p.função_privada(2); //
tentativa de invocação de método privado
//
da classePromíscua
.
}
Deve-se sempre tentar que as rotinas sejam normais (não membros). Se não o puderem ser, por questões de notação ou, mais importante, por necessidade de acesso aos membros privados de uma classe, façam-se membros (operações) dessa classe. Só se nenhuma das soluções anteriores for possível é que se pode usar o conceito de rotina amiga (mas antes de o fazer repense no problema que está a resolver: é provável que haja uma solução mais simples sem necessidade de promiscuidades...).
static
)static
. Estes atributos são apenas declarados dentro da
classe, sendo obrigatória a sua definição fora da classe (mas aí sem a
palavra chave static
).Da mesma forma é possível definir
operações que não precisam de ser
invocados através de qualquer instância da classe, i.e., operações sem instância
implícita. Mais uma vez utiliza-se a palavra chave static
para
esse efeito (de novo apenas na declaração da operação, e nunca na
definição do respectivo método). As invocações destas operações faz-se
normalmente usando o operador de resolução de âmbito ::
,
que deve ser precedido do nome da classe em causa.
Por exemplo, é possível que uma classe contenha um atributo de classe e privado que guarda quantas instâncias dessa classe existem em cada momento, e uma operação de classe que devolva o seu valor:
(Este programa escreve 0, 6 e 3 no ecrã. Porquê?)
class Contadora {
public:
Contadora();
~Contadora();
//
Declaração da operação de classe:
static int númeroDeInstânciasExistentes();
private:
//
Declaração do atributo de classe:
static int número_de_instâncias_existentes;
};
inline Contadora::Contadora()
{
++número_de_instâncias_existentes;
}
//
Isto é um destrutor! Tem o efeito inverso de um construtor.
inline Contadora::~Contadora()
{
--número_de_instâncias_existentes;
}
//
Definição do atributo de classe (inicialização com zero):
int Contadora::número_de_instâncias_existentes = 0;
//
Definição do método de classe correspondente à operação declarada:
inline int Contadora::númeroDeInstânciasExistentes()
{
return número_de_instâncias_existentes;
}
#include <iostream>
using namespace std;
int main()
{
cout << Contadora::númeroDeInstânciasExistentes() << endl;
Contadora a, b, c;
{
Contadora d, e, f;
cout << Contadora::númeroDeInstânciasExistentes() << endl;
}
cout << Contadora::númeroDeInstânciasExistentes() << endl;
}
(Quando aprender acerca dos construtores por cópia volte a esta classe e identifique um erro subtil que a classe contém, ou seja, um caso em que a contagem do número de instâncias pode falhar.)
O atributo número_de_instâncias_existentes
é partilhado por todas as instâncias da classe. Quando é construída
uma instância da classe Contadora
, o construtor incrementa o contador
de instâncias existentes, que é decrementado pelo destrutor sempre que uma
instância desta classe é destruída.