Existem algumas diferenças nas convenções de chamada em C ++ e Java. Em C ++, tecnicamente, existem apenas duas convenções: passagem por valor e passagem por referência, com alguma literatura incluindo uma terceira convenção de passagem por ponteiro (que é na verdade passagem por valor de um tipo de ponteiro). Além disso, você pode adicionar constância ao tipo de argumento, aprimorando a semântica.
Passe por referência
Passar por referência significa que a função receberá conceitualmente sua instância do objeto e não uma cópia dela. A referência é conceitualmente um alias para o objeto que foi usado no contexto de chamada e não pode ser nulo. Todas as operações executadas dentro da função se aplicam ao objeto fora da função. Esta convenção não está disponível em Java ou C.
Passagem por valor (e passagem por ponteiro)
O compilador irá gerar uma cópia do objeto no contexto de chamada e usar essa cópia dentro da função. Todas as operações executadas dentro da função são feitas na cópia, não no elemento externo. Esta é a convenção para tipos primitivos em Java.
Uma versão especial dele está passando um ponteiro (endereço do objeto) para uma função. A função recebe o ponteiro e todas e quaisquer operações aplicadas ao ponteiro são aplicadas à cópia (ponteiro), por outro lado, as operações aplicadas ao ponteiro sem referência serão aplicadas à instância do objeto nesse local da memória, portanto, a função pode ter efeitos colaterais. O efeito do uso da passagem por valor de um ponteiro para o objeto permitirá que a função interna modifique valores externos, como ocorre com a passagem por referência e também permitirá valores opcionais (passe um ponteiro nulo).
Esta é a convenção usada em C quando uma função precisa modificar uma variável externa e a convenção usada em Java com tipos de referência: a referência é copiada, mas o objeto referido é o mesmo: as alterações na referência / ponteiro não são visíveis fora a função, mas as alterações na memória apontada são.
Adicionando const à equação
No C ++, você pode atribuir constante a objetos ao definir variáveis, ponteiros e referências em diferentes níveis. Você pode declarar uma variável como constante, pode declarar uma referência para uma instância constante e pode definir todos os ponteiros para objetos constantes, ponteiros constantes para objetos mutáveis e ponteiros constantes para elementos constantes. Por outro lado, em Java, você pode definir apenas um nível de constante (palavra-chave final): o da variável (instância para tipos primitivos, referência para tipos de referência), mas não é possível definir uma referência para um elemento imutável (a menos que a própria classe seja imutável).
Isso é amplamente usado em convenções de chamada C ++. Quando os objetos são pequenos, você pode passar o objeto por valor. O compilador irá gerar uma cópia, mas essa cópia não é uma operação cara. Para qualquer outro tipo, se a função não alterar o objeto, você pode passar uma referência para uma instância constante (geralmente chamada referência constante) do tipo. Isso não copiará o objeto, mas passará para a função Mas, ao mesmo tempo, o compilador garantirá que o objeto não seja alterado dentro da função.
Regras de ouro
Estas são algumas regras básicas a seguir:
- Preferir passagem por valor para tipos primitivos
- Prefira passagem por referência com referências a constante para outros tipos
- Se a função precisar modificar o argumento, use passagem por referência
- Se o argumento for opcional, use passagem por ponteiro (para constante se o valor opcional não deve ser modificado)
Existem outros pequenos desvios dessas regras, a primeira delas é lidar com a propriedade de um objeto. Quando um objeto é alocado dinamicamente com new, ele deve ser desalocado com delete (ou suas versões []). O objeto ou função responsável pela destruição do objeto é considerado o proprietário do recurso. Quando um objeto alocado dinamicamente é criado em um trecho de código, mas a propriedade é transferida para um elemento diferente, geralmente isso é feito com semântica de passagem por ponteiro ou, se possível, com ponteiros inteligentes.
Nota
É importante insistir na importância da diferença entre as referências C ++ e Java. No C ++, as referências são conceitualmente a instância do objeto, não um acessador. O exemplo mais simples é implementar uma função de troca:
// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
Type tmp = a;
a = b;
b = tmp;
}
int main() {
Type a, b;
Type old_a = a, old_b = b;
swap( a, b );
assert( a == old_b );
assert( b == old_a );
}
A função de troca acima altera ambos os seus argumentos através do uso de referências. O código mais próximo em Java:
public class C {
// ...
public static void swap( C a, C b ) {
C tmp = a;
a = b;
b = tmp;
}
public static void main( String args[] ) {
C a = new C();
C b = new C();
C old_a = a;
C old_b = b;
swap( a, b );
// a and b remain unchanged a==old_a, and b==old_b
}
}
A versão Java do código modificará as cópias das referências internamente, mas não modificará os objetos reais externamente. As referências Java são ponteiros C sem aritmética de ponteiro que são passados por valor para funções.