Quais são os benefícios de passar por ponteiro sobre passar por referência em C ++?
Ultimamente, tenho visto vários exemplos que escolheram passar argumentos de função por ponteiros em vez de passar por referência. Existem benefícios em fazer isso?
Exemplo:
func(SPRITE *x);
com uma chamada de
func(&mySprite);
vs.
func(SPRITE &x);
com uma chamada de
func(mySprite);
c++
pointers
parameter-passing
pass-by-reference
Matt Pascoe
fonte
fonte
new
de criar um ponteiro e os problemas resultantes de propriedade.Respostas:
Um ponteiro pode receber um parâmetro NULL, um parâmetro de referência não. Se houver a chance de você passar "nenhum objeto", use um ponteiro em vez de uma referência.
Além disso, passar pelo ponteiro permite ver explicitamente no site da chamada se o objeto é passado por valor ou por referência:
fonte
func2 passes by reference
. Embora eu compreenda que você quis dizer que ele passa "por referência" de uma perspectiva de alto nível, implementada passando um ponteiro na perspectiva de nível de código, isso foi muito confuso (consulte stackoverflow.com/questions/13382356/… ).func(int& a)
não é C válido em nenhuma versão do padrão. Você provavelmente está compilando seus arquivos como C ++ por acidente.Passando pelo ponteiro
nothing
. Isso pode ser usado para fornecer argumentos opcionais.Passe por referência
string s = &str1 + &str2;
usar ponteiros.void f(const T& t); ... f(T(a, b, c));
:, ponteiros não podem ser usados assim, pois você não pode usar o endereço de um temporário.fonte
Gosto do raciocínio de um artigo de "cplusplus.com:"
O que retiro disso é que a principal diferença entre optar por usar um ponteiro ou parâmetro de referência é se NULL for um valor aceitável. É isso aí.
Se o valor é de entrada, saída, modificável etc. deve estar na documentação / comentários sobre a função, afinal.
fonte
Allen Holub, "Bastante Corda para Atirar no Pé", lista as 2 regras a seguir:
Ele lista vários motivos pelos quais as referências foram adicionadas ao C ++:
const
referências permitem que você tenha semântica de passagem por valor, evitando uma cópiaSeu ponto principal é que as referências não devem ser usadas como parâmetros de 'saída', porque no local da chamada não há indicação se o parâmetro é uma referência ou um valor. Portanto, sua regra é usar apenas
const
referências como argumentos.Pessoalmente, acho que essa é uma boa regra geral, pois fica mais claro quando um parâmetro é um parâmetro de saída ou não. No entanto, embora eu pessoalmente concorde com isso em geral, eu me permito ser influenciado pelas opiniões de outras pessoas da minha equipe se elas argumentarem pelos parâmetros de saída como referências (alguns desenvolvedores gostam imensamente).
fonte
Esclarecimentos sobre os posts anteriores:
As referências NÃO são uma garantia de obter um ponteiro não nulo. (Embora muitas vezes os tratemos como tal.)
Enquanto código horrivelmente ruim, como o levar para trás do código ruim do depósito de lenha , o seguinte será compilado e executado: (Pelo menos no meu compilador.)
O verdadeiro problema que tenho com as referências reside em outros programadores, doravante denominados IDIOTS , que alocam no construtor, desalocam no destruidor e não fornecem um construtor ou operador de cópia = ().
De repente, há um mundo de diferença entre foo (BAR bar) e foo (BAR & bar) . (A operação de cópia bit a bit automática é invocada. A desalocação no destruidor é invocada duas vezes.)
Felizmente, os compiladores modernos receberão essa dupla desalocação do mesmo ponteiro. 15 anos atrás, eles não fizeram. (Em gcc / g ++, use setenv MALLOC_CHECK_ 0 para revisar as formas antigas.) Resultando em DEC UNIX, na mesma memória sendo alocada para dois objetos diferentes. Muita diversão na depuração por lá ...
Mais praticamente:
fonte
*i
, seu programa tem um comportamento indefinido. Por exemplo, o compilador pode ver esse código e assumir "OK, esse código tem um comportamento indefinido em todos os caminhos de código, portanto, toda essa função deve estar inacessível". Então, assumirá que todos os ramos que levam a essa função não são utilizados. Essa é uma otimização realizada regularmente.A maioria das respostas aqui não aborda a ambiguidade inerente ao ter um ponteiro bruto em uma assinatura de função, em termos de expressar intenção. Os problemas são os seguintes:
O chamador não sabe se o ponteiro aponta para um único objeto ou para o início de uma "matriz" de objetos.
O chamador não sabe se o ponteiro "possui" a memória para a qual aponta. IE, se a função deve ou não liberar a memória. (
foo(new int)
- Isso é um vazamento de memória?).O chamador não sabe se
nullptr
pode ou não ser passado com segurança para a função.Todos esses problemas são resolvidos por referências:
As referências sempre se referem a um único objeto.
As referências nunca possuem a memória a que se referem, são apenas uma visão da memória.
As referências não podem ser nulas.
Isso faz das referências um candidato muito melhor para uso geral. No entanto, as referências não são perfeitas - há alguns problemas importantes a serem considerados.
&
operador para mostrar que realmente estamos passando um ponteiro. Por exemplo,int a = 5; foo(a);
não está claro aqui que a está sendo passado por referência e pode ser modificado.std::optional<T&>
não é válido (por boas razões), os ponteiros nos dão a nulidade que você deseja.Então, parece que quando queremos uma referência nula com indireção explícita, devemos buscar um
T*
direito? Errado!Abstrações
Em nosso desespero pela nulidade, podemos buscar
T*
e simplesmente ignorar todas as deficiências e ambigüidades semânticas listadas anteriormente. Em vez disso, devemos buscar o que o C ++ faz melhor: uma abstração. Se simplesmente escrevermos uma classe que envolve um ponteiro, obteremos a expressividade, bem como a nulidade e a indireta explícita.Essa é a interface mais simples que eu poderia criar, mas faz o trabalho com eficiência. Permite inicializar a referência, verificando se existe um valor e acessando o valor. Podemos usá-lo assim:
Alcançamos nossos objetivos! Vamos agora ver os benefícios, em comparação com o ponteiro bruto.
nullptr
pode ser passado, já que o autor da função está solicitando explicitamente umoptional_ref
Poderíamos tornar a interface mais complexa a partir daqui, como adicionar operadores de igualdade, uma interface monádica
get_or
emap
um método que obtém o valor ou gera uma exceção,constexpr
suporte. Isso pode ser feito por você.Em conclusão, em vez de usar ponteiros brutos, raciocine sobre o que esses ponteiros realmente significam em seu código e aproveite uma abstração de biblioteca padrão ou escreva sua própria. Isso melhorará seu código significativamente.
fonte
Na verdade não. Internamente, a passagem por referência é realizada essencialmente passando o endereço do objeto referenciado. Portanto, não há realmente nenhum ganho de eficiência ao passar um ponteiro.
Passar por referência tem um benefício, no entanto. É garantido que você tenha uma instância de qualquer objeto / tipo que esteja sendo passado. Se você passar um ponteiro, corre o risco de receber um ponteiro NULL. Usando a passagem por referência, você está enviando um NULL check implícito um nível acima para o chamador da sua função.
fonte