Um uso típico é conceder acesso a funções que fazem parte da interface da classe, mas graças a outras regras não podemos realmente fazer parte da classe. Inserters / extratores para iostreams são um exemplo clássico:
namespace whatever {
class something {
// ...
friend std::ostream &operator<<(std::ostream &os, something const &thing);
friend std::istream &operator>>(std::istream &is, something &thing);
};
}
Tornar esses operadores membros da classe não funcionará. Uma operação de E / S se parece com:
whatever::something thing;
std::cout << thing;
Para um operador implementado como uma função membro, isso seria resolvido como:
std::cout.operator<<(thing);
Ou seja, a função teria que ser membro de std::cout, não de something. Como não queremos modificar std::ostreamconstantemente, nossa única opção razoável é sobrecarregar o operador com uma função livre em vez de uma função membro. Isso nos deixa com duas possibilidades: ignorar completamente o encapsulamento e tornar público tudo na classe, ou mantê-lo privado, mas conceder acesso às poucas coisas que realmente precisam dele.
Tornar outra classe amiga é um pouco menos comum. Nesse caso, você normalmente cria algo da ordem de um módulo ou subsistema - um conjunto de classes que trabalham juntos e têm algum grau de acesso especial um ao outro que não é concedido ao mundo em geral.