Quando usar referências x ponteiros


381

Entendo a sintaxe e a semântica geral de ponteiros versus referências, mas como devo decidir quando é mais ou menos apropriado usar referências ou ponteiros em uma API?

Naturalmente, algumas situações precisam de uma ou de outra ( operator++precisa de um argumento de referência), mas, em geral, estou descobrindo que prefiro usar ponteiros (e const), pois a sintaxe é clara: as variáveis ​​estão sendo passadas destrutivamente.

Por exemplo, no seguinte código:

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

Com o ponteiro, é sempre (mais) óbvio o que está acontecendo; portanto, para APIs e afins, onde a clareza é uma grande preocupação, os ponteiros não são mais apropriados que as referências? Isso significa que as referências devem ser usadas apenas quando necessário (por exemplo operator++)? Há alguma preocupação de desempenho com um ou outro?

EDITAR (ATUALIZADO):

Além de permitir valores NULL e lidar com matrizes brutas, parece que a escolha se resume à preferência pessoal. Aceitei a resposta abaixo que faz referência ao Guia de estilo C ++ do Google , pois apresenta a visão de que "As referências podem ser confusas, pois possuem sintaxe de valor, mas semântica de ponteiro".

Devido ao trabalho adicional necessário para limpar argumentos de ponteiro que não devem ser NULL (por exemplo add_one(0), chamará a versão do ponteiro e interromperá durante o tempo de execução), faz sentido do ponto de vista da manutenção usar referências onde um objeto DEVE estar presente, embora seja uma vergonha perder a clareza sintática.


4
Parece que você já tomou sua decisão sobre qual usar quando. Pessoalmente, prefiro transmitir o objeto em que estou atuando, esteja ou não modificando-o. Se uma função usa um ponteiro, isso indica que ela está agindo nos ponteiros, ou seja, usá-los como iteradores em uma matriz.
Benjamin Lindley

11
@ Schnommus: Justo, eu uso principalmente o TextMate. Ainda assim, acho preferível que o significado seja óbvio à primeira vista.
connec

4
Sobre o add_one(a);que não está claro que aserá modificado? Diz exatamente o código: adicione um .
GManNickG

32
@connec: o guia de estilo do Google C ++ não é considerado um bom guia de estilo do C ++. É um guia de estilo para trabalhar com a antiga base de código C ++ do Google (ou seja, boa para as coisas deles). Aceitar uma resposta com base nisso não ajuda ninguém. Apenas lendo seus comentários e explicações, você chegou a esta pergunta com uma opinião já definida e está apenas procurando outras pessoas para confirmar sua opinião. Como resultado, você está baseando a pergunta e respondendo ao que deseja / espera ouvir.
Martin York

11
Isso é simplesmente corrigido nomeando o método addOneTo(...). Se não é isso que você quer fazer, basta olhar para a declaração.
stefan

Respostas:


296

Use referência sempre que puder, indicadores sempre que precisar.

Evite ponteiros até não conseguir.

A razão é que os indicadores tornam as coisas mais difíceis de seguir / ler, menos seguras e muito mais perigosas do que qualquer outra construção.

Portanto, a regra geral é usar ponteiros apenas se não houver outra opção.

Por exemplo, retornar um ponteiro para um objeto é uma opção válida quando a função pode retornar nullptr em alguns casos e supõe-se que sim. Dito isto, uma opção melhor seria usar algo semelhante boost::optional.

Outro exemplo é usar ponteiros para a memória não processada para manipulações de memória específicas. Isso deve estar oculto e localizado em partes muito estreitas do código, para ajudar a limitar as partes perigosas de toda a base de código.

No seu exemplo, não faz sentido usar um ponteiro como argumento porque:

  1. se você fornecer nullptrcomo argumento, estará em terreno de comportamento indefinido;
  2. a versão do atributo de referência não permite (sem truques fáceis de identificar) o problema com 1.
  3. a versão do atributo de referência é mais simples de entender para o usuário: você precisa fornecer um objeto válido, não algo que possa ser nulo.

Se o comportamento da função precisar trabalhar com ou sem um determinado objeto, o uso de um ponteiro como atributo sugere que você pode passar nullptrcomo argumento e isso é bom para a função. Esse é o tipo de contrato entre o usuário e a implementação.


49
Não tenho certeza se os ponteiros dificultam a leitura? É um conceito bastante simples e deixa claro quando é provável que algo seja modificado. Se alguma coisa que eu diria que é mais difícil de ler quando não há indicação do que está acontecendo, por que add_one(a)não retornar o resultado, em vez de defini-lo por referência?
connec

46
@connec: Se add_one(a)é confuso, é porque é nomeado incorretamente. add_one(&a)teria a mesma confusão, só que agora você pode incrementar o ponteiro e não o objeto. add_one_inplace(a)evitaria toda confusão.
Nicol Bolas

20
Um ponto, as referências podem se referir à memória que pode desaparecer tão facilmente quanto os ponteiros. Portanto, eles não são necessariamente mais seguros do que indicadores. As referências persistentes e passantes podem ser tão perigosas quanto os ponteiros.
Doug T.

6
@Klaim eu quis dizer ponteiros brutos. Eu quis dizer que C ++ tem ponteiros NULLe nullptr, e os possui por um motivo. E não é um conselho bem-considerado ou realista que dê "nunca use ponteiros" e / ou "nunca use NULL, sempre use boost::optional". Isso é loucura. Não me interpretem mal, os ponteiros brutos são necessários com menos frequência em C ++ do que em C, mas, ainda assim, são úteis, não são tão "perigosos" quanto algumas pessoas em C ++ gostam de afirmar (isso também é um exagero): quando é mais fácil usar apenas um ponteiro e return nullptr;indicar um valor ausente ... Por que importar todo o Boost?

5
@Klaim "usar NULL é uma prática ruim" - agora isso é ridículo. E ifé obsoleto e deve-se usar while() { break; }, certo? Além disso, não se preocupe, eu já vi e trabalhei com grandes bases de código e, sim, se você é descuidado , a propriedade é um problema. Não, se você seguir as convenções, use-as de forma consistente, comente e documente seu código. Mas, afinal, eu deveria usar C porque sou burra demais para C ++, certo?

62

As performances são exatamente as mesmas, pois as referências são implementadas internamente como ponteiros. Portanto, você não precisa se preocupar com isso.

Não existe uma convenção geralmente aceita sobre quando usar referências e ponteiros. Em alguns casos, você deve retornar ou aceitar referências (construtor de cópias, por exemplo), mas fora isso, você é livre para fazer o que quiser. Uma convenção bastante comum que encontrei é usar referências quando o parâmetro deve fazer referência a um objeto existente e ponteiros quando um valor NULL estiver ok.

Algumas convenções de codificação (como a do Google ) prescrevem que sempre se deve usar ponteiros ou referências const, porque as referências têm um pouco de sintaxe pouco clara: elas têm comportamento de referência, mas valorizam a sintaxe.


10
Apenas para adicionar um pouco a isso, o guia de estilo do Google diz que os parâmetros de entrada para funções devem ser referências constantes e as saídas devem ser ponteiros. Eu gosto disso porque fica muito claro quando você lê uma assinatura de função o que é uma entrada e o que é uma saída.
Dan

44
@ Dan: O guia de estilo do Google é para o código antigo do Google e não deve ser usado para codificação moderna. De fato, é um estilo de codificação bastante ruim para um novo projeto.
GManNickG

13
@connec: Deixe-me colocar desta maneira: null é um valor de ponteiro perfeitamente válido . Em qualquer lugar que houver um ponteiro, eu posso dar o valor nulo. Ergo sua segunda versão add_oneé quebrado : add_one(0); // passing a perfectly valid pointer value, kaboom. Você precisa verificar se é nulo. Algumas pessoas responderão: "bem, apenas documentarei que minha função não funciona com nulo". Tudo bem, mas você derrota o objetivo da pergunta: se você for procurar na documentação para verificar se nulo está bom, também verá a declaração da função .
GManNickG

8
Se fosse uma referência, você veria esse o caso. Porém, essa réplica perde o sentido: as referências reforçam no nível da linguagem que se refere a um objeto existente, e não possivelmente nulo, enquanto os ponteiros não têm essa restrição. Acho claro que a imposição no nível da linguagem é mais poderosa e menos propensa a erros do que a imposição no nível da documentação. Alguns tentarão responder a isso dizendo: "Olha, referência nula:. int& i = *((int*)0);Esta não é uma resposta válida. O problema no código anterior está no uso do ponteiro, não na referência . As referências nunca são nulas, ponto.
GManNickG

12
Olá, eu vi falta de advogados de idiomas nos comentários, então deixe-me remediar: as referências são geralmente implementadas por ponteiros, mas o padrão não diz isso. Uma implementação usando algum outro mecanismo seria 100% de reclamação.
Thomas Bonini

34

Do C ++ FAQ Lite -

Use referências quando puder e ponteiros quando precisar.

As referências geralmente são preferidas aos ponteiros sempre que você não precisar "recolocar". Isso geralmente significa que as referências são mais úteis na interface pública de uma classe. As referências geralmente aparecem na capa de um objeto e ponteiros no interior.

A exceção acima é onde o parâmetro ou valor de retorno de uma função precisa de uma referência "sentinela" - uma referência que não se refere a um objeto. Isso geralmente é melhor retornando / pegando um ponteiro e atribuindo ao ponteiro NULL esse significado especial (as referências devem sempre usar alias de objetos, não um ponteiro NULL desreferenciado).

Nota: Os programadores da linha C antiga às vezes não gostam de referências, pois fornecem semântica de referência que não está explícita no código do chamador. Após alguma experiência em C ++, no entanto, percebe-se rapidamente que essa é uma forma de ocultar informações, que é um ativo e não um passivo. Por exemplo, os programadores devem escrever código no idioma do problema e não no idioma da máquina.


11
Eu acho que você poderia argumentar que, se você estiver usando uma API, deve estar familiarizado com o que ela faz e saber se o parâmetro transmitido é ou não modificado ou não ... algo a considerar, mas acho que estou concordando com os programadores C ( embora eu tenha pouca experiência em C). Eu acrescentaria que essa sintaxe mais clara é benéfica para programadores e máquinas.
connec

11
@connec: Claro que o programador C está correto para o idioma deles. Mas não cometa o erro de tratar C ++ como C. É uma linguagem completamente diferente. Se você trata C ++ como C, acaba escrevendo o que é referenciado de maneira igual como C with class(que não é C ++).
Martin York

15

Minha regra de ouro é:

  • Use ponteiros para parâmetros de saída ou entrada / saída. Portanto, pode-se ver que o valor será alterado. (Você deve usar &)
  • Use ponteiros se o parâmetro NULL for um valor aceitável. (Verifique constse é um parâmetro de entrada)
  • Use referências para o parâmetro recebido se não puder ser NULL e não for um tipo primitivo ( const T&).
  • Use ponteiros ou ponteiros inteligentes ao retornar um objeto recém-criado.
  • Use ponteiros ou ponteiros inteligentes como membros de estrutura ou classe em vez de referências.
  • Use referências para alias (por exemplo int &current = someArray[i])

Independentemente de qual você usar, não se esqueça de documentar suas funções e o significado de seus parâmetros se eles não forem óbvios.


14

Isenção de responsabilidade: além do fato de que as referências não podem ser NULL nem "rebote" (o que significa que elas não podem alterar o objeto do qual são o apelido), tudo se resume a uma questão de gosto, por isso não vou dizer "isto é melhor".

Dito isso, eu discordo de sua última declaração no post, pois não acho que o código perca clareza com referências. No seu exemplo,

add_one(&a);

pode ser mais claro que

add_one(a);

pois você sabe que provavelmente o valor de a mudará. Por outro lado, porém, a assinatura da função

void add_one(int* const n);

também não está claro: n será um único número inteiro ou uma matriz? Às vezes, você só tem acesso a cabeçalhos (mal documentados) e assinaturas como

foo(int* const a, int b);

não são fáceis de interpretar à primeira vista.

Portanto, as referências são tão boas quanto indicadores quando não é necessária (re) alocação ou religação (no sentido explicado anteriormente). Além disso, se um desenvolvedor usa apenas ponteiros para matrizes, as assinaturas de funções são um pouco menos ambíguas. Sem mencionar o fato de que a sintaxe dos operadores é muito mais legível com as referências.


Obrigado pela demonstração clara de onde as duas soluções ganham e perdem a clareza. Eu estava inicialmente no campo dos ponteiros, mas isso faz muito sentido.
Zach Beavon-Collin

12

Como outros já respondidos: Sempre use referências, a menos que a variável seja NULL/ nullptrseja realmente um estado válido.

O ponto de vista de John Carmack sobre o assunto é semelhante:

Ponteiros NULL são o maior problema em C / C ++, pelo menos em nosso código. O uso duplo de um valor único como sinalizador e endereço causa um número incrível de problemas fatais. Referências em C ++ devem ser favorecidas sobre ponteiros sempre que possível; embora uma referência seja "realmente" apenas um ponteiro, ela tem o contrato implícito de não ser NULL. Execute verificações NULL quando os ponteiros forem transformados em referências; depois, você poderá ignorar o problema.

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

Editar 2012-03-13

O usuário Bret Kuhns observa corretamente:

O padrão C ++ 11 foi finalizado. Acho que está na hora de mencionar que a maioria dos códigos deve se sair perfeitamente bem com uma combinação de referências, shared_ptr e unique_ptr.

É verdade, mas a questão ainda permanece, mesmo ao substituir ponteiros brutos por ponteiros inteligentes.

Por exemplo, ambos std::unique_ptre std::shared_ptrpodem ser construídos como ponteiros "vazios" por meio de seu construtor padrão:

... significando que usá-los sem verificar se não estão vazios corre o risco de um acidente, que é exatamente o que trata a discussão de J. Carmack.

E então, temos o divertido problema de "como passamos um ponteiro inteligente como parâmetro de função?"

A resposta de Jon para a pergunta C ++ - passando referências para boost :: shared_ptr , e os comentários a seguir mostram que, mesmo assim, passar um ponteiro inteligente por cópia ou por referência não é tão claro quanto se gostaria (eu sou a favor de " por referência "por padrão, mas posso estar errado).


11
O padrão C ++ 11 foi finalizado. Acho que está na hora de mencionar que a maioria dos códigos deve se sair perfeitamente bem com uma combinação de referências shared_ptr, e unique_ptr. A semântica de propriedade e as convenções de parâmetros in / out são atendidas por uma combinação dessas três partes e const'ness. Quase não há necessidade de ponteiros brutos em C ++, exceto quando se lida com código legado e algoritmos muito otimizados. As áreas em que são usadas devem ser o mais encapsuladas possível e converter qualquer ponteiro bruto no equivalente "moderno" semanticamente apropriado.
Bret Kuhns 13/09/12

11
Muitas vezes, os ponteiros inteligentes não devem ser repassados, mas devem ser testados quanto à nulidade e, em seguida, seu objeto contido passado por referência. O único momento em que você deve passar um ponteiro inteligente é quando você está transferindo (unique_ptr) ou compartilhando (shared_ptr) a propriedade com outro objeto.
Luke Worth

@ povman: concordo plenamente: se a propriedade não faz parte da interface (e, a menos que esteja prestes a ser modificada, não deveria ser), não devemos passar um ponteiro inteligente como parâmetro (ou valor de retorno). A coisa fica um pouco mais complicada quando a propriedade faz parte da interface. Por exemplo, o Sutter / Meyers debate sobre como passar um unique_ptr como parâmetro: por cópia (Sutter) ou por referência de valor r (Meyers)? Um antipattern depende de passar em torno de um ponteiro para um shared_ptr global, com o risco de que ponteiro sendo invalidado (esta solução é copiar o ponteiro inteligente na pilha)
paercebal

7

Não é uma questão de gosto. Aqui estão algumas regras definitivas.

Se você quiser se referir a uma variável declarada estaticamente dentro do escopo em que foi declarada, use uma referência C ++ e ela será perfeitamente segura. O mesmo se aplica a um ponteiro inteligente declarado estaticamente. A passagem de parâmetros por referência é um exemplo desse uso.

Se você quiser se referir a algo de um escopo mais amplo que o escopo em que foi declarado, use um ponteiro inteligente contado como referência para que seja perfeitamente seguro.

Você pode se referir a um elemento de uma coleção com uma referência para conveniência sintática, mas não é seguro; o elemento pode ser excluído a qualquer momento.

Para manter com segurança uma referência a um elemento de uma coleção, você deve usar um ponteiro inteligente contado como referência.


5

Qualquer diferença de desempenho seria tão pequena que não justificaria usar uma abordagem menos clara.

Primeiro, um caso que não foi mencionado em que as referências geralmente são superiores são as constreferências. Para tipos não simples, passar umconst reference evita a criação de um temporário e não causa a confusão com a qual você se preocupa (porque o valor não é modificado). Aqui, forçar uma pessoa a passar um ponteiro causa a mesma confusão com a qual você está preocupado, pois ver o endereço recebido e passado para uma função pode fazer você pensar que o valor foi alterado.

De qualquer forma, basicamente concordo com você. Não gosto de funções que façam referências para modificar seu valor quando não é muito óbvio que é isso que a função está fazendo. Eu também prefiro usar ponteiros nesse caso.

Quando você precisa retornar um valor em um tipo complexo, eu tendem a preferir referências. Por exemplo:

bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative

Aqui, o nome da função deixa claro que você está recebendo informações de volta em uma matriz. Portanto, não há confusão.

As principais vantagens das referências são que elas sempre contêm um valor válido, são mais limpas do que ponteiros e suportam polimorfismos sem precisar de nenhuma sintaxe extra. Se nenhuma dessas vantagens se aplicar, não há motivo para preferir uma referência a um ponteiro.


4

Copiado do wiki -

Uma conseqüência disso é que, em muitas implementações, operando em uma variável com vida útil automática ou estática por meio de uma referência, embora sintaticamente semelhante ao acesso direto a ela, pode envolver operações de desreferência ocultas que são caras. As referências são um recurso sintaticamente controverso do C ++ porque obscurecem o nível de indireção de um identificador; ou seja, diferentemente do código C, em que os ponteiros geralmente se destacam sintaticamente, em um grande bloco de código C ++ pode não ser imediatamente óbvio se o objeto acessado for definido como uma variável local ou global ou se é uma referência (ponteiro implícito) a em outro local, principalmente se o código combinar referências e ponteiros. Esse aspecto pode dificultar a leitura e a depuração do código C ++ mal escrito (consulte Alias).

Concordo 100% com isso, e é por isso que acredito que você só deve usar uma referência quando tiver um bom motivo para fazê-lo.


Eu também concordo em grande parte, no entanto, estou considerando que a perda de proteção interna contra ponteiros NULL é um pouco cara para preocupações puramente sintáticas, especialmente porque - embora mais explícita - a sintaxe do ponteiro é bastante feia de qualquer forma.
connec

Suponho que a circunstância seria um fator importante também. Eu acho que tentar usar referências quando a base de código atual usa predominantemente ponteiros seria uma má idéia. Se você espera que seus para ser referências então o fato de que seu modo implícito é menos importante talvez ..
user606723

3

Pontos a serem lembrados:

  1. Ponteiros podem ser NULL, referências não podem ser NULL.

  2. As referências são mais fáceis de usar, constpodem ser usadas para uma referência quando não queremos alterar valor e precisamos apenas de uma referência em uma função.

  3. Ponteiro usado com um *tempo referências usadas com um &.

  4. Use ponteiros quando a operação aritmética do ponteiro for necessária.

  5. Você pode ter ponteiros para um tipo de nulo, int a=5; void *p = &a;mas não pode ter uma referência a um tipo de nulo.

Ponteiro versus referência

void fun(int *a)
{
    cout<<a<<'\n'; // address of a = 0x7fff79f83eac
    cout<<*a<<'\n'; // value at a = 5
    cout<<a+1<<'\n'; // address of a increment by 4 bytes(int) = 0x7fff79f83eb0
    cout<<*(a+1)<<'\n'; // value here is by default = 0
}
void fun(int &a)
{
    cout<<a<<'\n'; // reference of original a passed a = 5
}
int a=5;
fun(&a);
fun(a);

Veredicto quando usar o que

Ponteiro : Para matriz, lista de links, implementações em árvore e aritmética de ponteiros.

Referência : em parâmetros de função e tipos de retorno.


2

Há um problema com a regra " usar referências sempre que possível " e surge se você deseja manter a referência para uso posterior. Para ilustrar isso com um exemplo, imagine que você tenha as seguintes classes.

class SimCard
{
    public:
        explicit SimCard(int id):
            m_id(id)
        {
        }

        int getId() const
        {
            return m_id;
        }

    private:
        int m_id;
};

class RefPhone
{
    public:
        explicit RefPhone(const SimCard & card):
            m_card(card)
        {
        }

        int getSimId()
        {
            return m_card.getId();
        }

    private:
        const SimCard & m_card;
};

A princípio, pode parecer uma boa idéia ter um parâmetro no RefPhone(const SimCard & card)construtor passado por uma referência, porque impede a passagem de ponteiros errados / nulos para o construtor. De alguma forma, incentiva a alocação de variáveis ​​na pilha e aproveita os benefícios do RAII.

PtrPhone nullPhone(0);  //this will not happen that easily
SimCard * cardPtr = new SimCard(666);  //evil pointer
delete cardPtr;  //muahaha
PtrPhone uninitPhone(cardPtr);  //this will not happen that easily

Mas então os temporários vêm para destruir seu mundo feliz.

RefPhone tempPhone(SimCard(666));   //evil temporary
//function referring to destroyed object
tempPhone.getSimId();    //this can happen

Portanto, se você cegar cegamente às referências, reduz a possibilidade de passar ponteiros inválidos para a possibilidade de armazenar referências a objetos destruídos, que basicamente têm o mesmo efeito.

editar: observe que eu me apeguei à regra "Use referências sempre que puder, indicadores sempre que precisar. Evite indicadores até não conseguir". da resposta mais votada e aceita (outras respostas também sugerem isso). Embora deva ser óbvio, o exemplo não é mostrar que as referências são ruins. No entanto, eles podem ser mal utilizados, como ponteiros e podem trazer suas próprias ameaças ao código.


Existem as seguintes diferenças entre ponteiros e referências.

  1. Quando se trata de passar variáveis, passar por referência parece passar por valor, mas possui semântica de ponteiro (age como ponteiro).
  2. A referência não pode ser inicializada diretamente como 0 (nulo).
  3. A referência (objeto de referência, não referenciado) não pode ser modificada (equivalente ao ponteiro "* const").
  4. A referência const pode aceitar parâmetro temporário.
  5. As referências const locais prolongam a vida útil dos objetos temporários

Levando isso em consideração, minhas regras atuais são as seguintes.

  • Use referências para parâmetros que serão usados ​​localmente dentro de um escopo de função.
  • Use ponteiros quando 0 (nulo) for o valor aceitável do parâmetro ou você precisar armazenar o parâmetro para uso posterior. Se 0 (nulo) for aceitável, estou adicionando o sufixo "_n" ao parâmetro, use o ponteiro protegido (como QPointer no Qt) ou apenas documente. Você também pode usar ponteiros inteligentes. É necessário ter ainda mais cuidado com os ponteiros compartilhados do que com os ponteiros normais (caso contrário, você poderá acabar com vazamentos de memória de design e bagunça de responsabilidade).

3
O problema com o seu exemplo não é que as referências não sejam seguras, mas que você esteja confiando em algo fora do escopo da sua instância de objeto para manter seus membros privados vivos. const SimCard & m_card;é apenas um código mal escrito.
Plamenko

@ plamenko Receio que você não entenda o propósito do exemplo. Se const SimCard & m_cardestá correto ou não, depende do contexto. A mensagem nesta postagem não é que as referências sejam inseguras (embora possam ser se alguém se esforçar). A mensagem é que você não deve ficar cego no mantra "usar referências sempre que possível". Exemplo é um resultado do uso agressivo da doutrina "use referências sempre que possível". Isso deve ficar claro.
doc

Há duas coisas que me incomodam com sua resposta, porque acho que isso pode induzir alguém a tentar aprender mais sobre o assunto. 1. A postagem é unidirecional e é fácil ter a impressão de que as referências são ruins. Você forneceu apenas um exemplo de como não usar referências. 2. Você não esclareceu em seu exemplo o que há de errado com isso. Sim, temporário será destruído, mas não foi a linha que estava errada, é a implementação da classe.
Plamenko

Você praticamente nunca deve ter membros como const SimCard & m_card. Se você quiser ser eficiente com os temporários, adicione o explicit RefPhone(const SimCard&& card)construtor.
Plamenko

@ Plamenko se você não pode ler com alguma compreensão básica, então você tem um problema maior do que apenas ser enganado pelo meu post. Não sei como poderia ser mais claro. Veja a primeira frase. Há um problema com o mantra "use referências sempre que possível"! Onde, no meu post, você encontrou uma declaração de que as referências são ruins? No final do meu post, você escreveu onde usar referências, então como chegou a essas conclusões? Esta não é uma resposta direta à pergunta?
doc

1

A seguir estão algumas diretrizes.

Uma função usa dados passados ​​sem modificá-los:

  1. Se o objeto de dados for pequeno, como um tipo de dados interno ou uma estrutura pequena, transmita-o por valor.

  2. Se o objeto de dados for uma matriz, use um ponteiro, porque essa é sua única opção. Faça do ponteiro um ponteiro para const.

  3. Se o objeto de dados for uma estrutura de bom tamanho, use um ponteiro const ou uma referência const para aumentar a eficiência do programa. Você economiza tempo e espaço necessários para copiar uma estrutura ou um design de classe. Faça o ponteiro ou a referência const.

  4. Se o objeto de dados é um objeto de classe, use uma referência const. A semântica do design de classe geralmente exige o uso de uma referência, que é o principal motivo pelo qual o C ++ adicionou esse recurso.

Uma função modifica os dados na função de chamada:

1.Se o objeto de dados for um tipo de dados interno, use um ponteiro. Se você localizar código como fixit (& x), em que x é um int, é bastante claro que essa função pretende modificar x.

2.Se o objeto de dados for uma matriz, use sua única opção: um ponteiro.

3.Se o objeto de dados for uma estrutura, use uma referência ou um ponteiro.

4.Se o objeto de dados for um objeto de classe, use uma referência.

Obviamente, essas são apenas diretrizes e pode haver razões para fazer escolhas diferentes. Por exemplo, cin usa referências para tipos básicos, para que você possa usar cin >> n em vez de cin >> & n.


0

As referências são mais limpas e fáceis de usar, além de ocultar melhor as informações. As referências não podem ser reatribuídas, no entanto. Se você precisar apontar primeiro para um objeto e depois para outro, use um ponteiro. As referências não podem ser nulas; portanto, se houver alguma chance de o objeto em questão ser nulo, você não deve usar uma referência. Você deve usar um ponteiro. Se você deseja lidar com a manipulação de objetos por conta própria, ou seja, se você deseja alocar espaço de memória para um objeto no Heap e não na Stack, use o Ponteiro

int *pInt = new int; // allocates *pInt on the Heap

0

Seu exemplo escrito corretamente deve parecer

void add_one(int& n) { n += 1; }
void add_one(int* const n)
{
  if (n)
    *n += 1;
}

É por isso que as referências são preferíveis, se possível ...


-1

Apenas colocando minha moeda. Acabei de realizar um teste. Um sorrateiro nisso. Eu apenas deixei o g ++ criar os arquivos de montagem do mesmo miniprograma usando ponteiros em comparação ao uso de referências. Ao olhar para a saída, eles são exatamente os mesmos. Diferente da simbologia. Portanto, analisando o desempenho (em um exemplo simples), não há problema.

Agora, no tópico ponteiros vs referências. IMHO Eu acho que a clareza está acima de tudo. Assim que leio o comportamento implícito, meus dedos começam a se enrolar. Concordo que é um bom comportamento implícito que uma referência não possa ser NULL.

Desreferenciar um ponteiro NULL não é o problema. ele travará seu aplicativo e será fácil depurar. Um problema maior são os ponteiros não inicializados que contêm valores inválidos. Isso provavelmente resultará em corrupção de memória, causando um comportamento indefinido sem uma origem clara.

É aqui que acho que as referências são muito mais seguras do que indicadores. E eu concordo com uma declaração anterior, de que a interface (que deve ser claramente documentada, veja o projeto por contrato, Bertrand Meyer) define o resultado dos parâmetros para uma função. Agora, levando tudo isso em consideração, minhas preferências vão para o uso de referências sempre que possível.


-2

Para ponteiros, você precisa apontar para alguma coisa, para que os ponteiros custem espaço na memória.

Por exemplo, uma função que usa um ponteiro inteiro não aceita a variável inteira. Portanto, você precisará criar um ponteiro para que o primeiro passe para a função.

Quanto a uma referência, não custará memória. Você tem uma variável inteira e pode transmiti-la como uma variável de referência. É isso aí. Você não precisa criar uma variável de referência especialmente para ela.


4
Não. Uma função que usa um ponteiro não requer a alocação de uma variável de ponteiro: você pode passar um temporário &address. Uma referência certamente custará memória se for membro de um objeto e, além disso, todos os compiladores existentes realmente implementam referências como endereços, para que você também não salve nada em termos de passagem de parâmetros ou desreferenciamento.
25416 #
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.