Eu sou novo na programação C ++, mas tenho experiência em Java. Preciso de orientações sobre como passar objetos para funções em C ++.
Preciso passar ponteiros, referências ou valores sem ponteiro e sem referência? Lembro que em Java não existem problemas, pois passamos apenas a variável que contém referência aos objetos.
Seria ótimo se você também pudesse explicar onde usar cada uma dessas opções.
c++
pointers
pass-by-reference
pass-by-value
c++-faq
Rakesh K
fonte
fonte
Respostas:
Regras práticas para C ++ 11:
Passe por valor , exceto quando
const
referência ,const
referência sem valor ,const
referência ou não.)Passar pelo ponteiro praticamente nunca é aconselhável. Os parâmetros opcionais são melhor expressos como um
std::optional
(boost::optional
para bibliotecas std mais antigas) e o alias é feito bem por referência.A semântica de movimentação do C ++ 11 torna a passagem e o retorno por valor muito mais atraentes, mesmo para objetos complexos.
Regras básicas para C ++ 03:
Passe argumentos por
const
referência , exceto quandoconst
referênciaNULL
/0
/nullptr
; aplique a regra anterior para determinar se você deve passar por um ponteiro para umconst
argumento(aqui, "passar por valor" é chamado "passar por cópia", porque passar por valor sempre cria uma cópia em C ++ 03)
Há mais do que isso, mas essas poucas regras para iniciantes o levarão muito longe.
fonte
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:
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:
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:
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.
fonte
Existem vários casos a considerar.
Parâmetro modificado (parâmetros "out" e "in / out")
Este caso é principalmente sobre estilo: você deseja que o código pareça chamar (obj) ou chamar (& obj) ? No entanto, há dois pontos em que a diferença importa: o caso opcional, abaixo, e você deseja usar uma referência ao sobrecarregar os operadores.
... e opcional
Parâmetro não modificado
Este é o caso interessante. A regra geral é "copiar barato" tipos são passados por valor - geralmente são tipos pequenos (mas nem sempre) - enquanto outros são passados por const ref. No entanto, se você precisar fazer uma cópia dentro de sua função, deve passar por valor . (Sim, isso expõe um pouco de detalhes da implementação. C'est le C ++. )
... e opcional
Aqui há a menor diferença entre todas as situações; portanto, escolha o que facilitar sua vida.
Const por valor é um detalhe de implementação
Essas declarações são exatamente a mesma função! Ao passar por valor, const é puramente um detalhe de implementação. Experimente:
fonte
const
ser uma implementação ao passar por valor.Passe por valor:
Passe variáveis por valor quando a função precisar de um isolamento completo do ambiente, ou seja, para impedir que a função modifique a variável original e também para outros threads modificarem seu valor enquanto a função estiver sendo executada.
A desvantagem são os ciclos da CPU e a memória extra gasta para copiar o objeto.
Passe por referência const:
Este formulário emula o comportamento de passagem por valor ao remover a sobrecarga de cópia. A função obtém acesso de leitura ao objeto original, mas não pode modificar seu valor.
A desvantagem é a segurança do thread: qualquer alteração feita no objeto original por outro thread aparecerá dentro da função enquanto ela ainda estiver em execução.
Passe por referência não const:
Use isso quando a função precisar gravar algum valor na variável, que será usada pelo chamador.
Assim como o caso de referência const, isso não é seguro para threads.
Passe pelo ponteiro const:
Funcionalmente, o mesmo que passar por referência constante, exceto pela sintaxe diferente, além do fato de que a função de chamada pode passar o ponteiro NULL para indicar que não possui dados válidos para passar.
Não é seguro para threads.
Passe pelo ponteiro não const:
Semelhante à referência não-const. O chamador normalmente define a variável como NULL quando a função não deve gravar um valor novamente. Essa convenção é vista em muitas APIs glibc. Exemplo:
Assim como todos passam por referência / ponteiro, não são seguros para threads.
fonte
Como ninguém mencionou a adição, quando você passa um objeto para uma função em c ++, o construtor de cópia padrão do objeto é chamado se você não tiver um que crie um clone do objeto e depois o passe para o método, portanto quando você altera os valores do objeto que refletirão na cópia do objeto em vez do objeto original, esse é o problema em c ++. Portanto, se você fizer com que todos os atributos da classe sejam ponteiros, os construtores de cópia copiarão os endereços dos atributos do ponteiro, portanto, quando o método invoca o objeto que manipula os valores armazenados nos endereços dos atributos do ponteiro, as alterações também refletem no objeto original que é passado como parâmetro, para que isso possa se comportar como um Java, mas não esqueça que toda a sua classe atributos devem ser ponteiros, você também deve alterar os valores dos ponteiros,ficará muito claro com a explicação do código.
Mas isso não é uma boa ideia, pois você acabará escrevendo muito código envolvendo ponteiros, propensos a vazamentos de memória e não se esqueça de chamar destruidores. E para evitar isso, o c ++ possui construtores de cópia nos quais você cria uma nova memória quando os objetos que contêm ponteiros são passados para argumentos de função que param de manipular dados de outros objetos, o Java passa por valor e o valor é referência, portanto, não requer construtores de cópia.
fonte
Existem três métodos para passar um objeto para uma função como parâmetro:
Siga o exemplo a seguir:
Resultado:
fonte
A seguir estão as maneiras de passar argumentos / parâmetros para funcionar em C ++.
1. por valor.
2. por referência.
3. por objeto.
fonte