A implementação da hierarquia de classes influencia no funcionamento do código. Em outras palavras, se Gerente for subclasse de Funcionário, o objeto g1 herdará de Funcionário. Por outro lado, se a classe Caixa também for subclasse de Funcionário (ou seja, Caixa e Gerente são classes irmãs), ao implementar o código será mostrado um erro em tempo de compilação.
Respondendo a pergunta inicial, você não é obrigado a referenciar objetos da maneira como fez. Uma outra opção é fazê-lo da seguinte forma:
// Gerente g1 = new Gerente();
// Caixa c1 = new Caixa();
A diferença é que declarar de forma mais restritiva (como eu fiz acima), restringe o objeto g1 a ser instâcia de Gerente durante toda a vida do código. Por outro lado, ao instanciar um objeto de forma mais genérica (Funcionario x = new Caixa();), possibilita que o objeto x possa se tornar, ao decorrer do tempo, instâcia de Gerente.
Espero lhe ter ajudado. Bons estudos.