Pergunta

É melhor em C ++ para passar por valor ou passar por referência constante?

Eu estou querendo saber o que é melhor prática. Eu percebo que passam por constante referência deve fornecer para um melhor desempenho no programa, porque você não está fazendo uma cópia da variável.

Foi útil?

Solução

Ela costumava ser geralmente recomendado melhores práticas 1 para utilização passagem por ref const para todos os tipos , exceto para builtin tipos (char, int, double, etc.), para iterators e para a função objetos (lambdas, aulas decorrentes std::*_function).

Isso foi especialmente verdadeiro antes da existência de Mover semântica . A razão é simples:. Se você passou por valor, uma cópia do objeto tinha de ser feita e, exceto para objetos muito pequenos, este é sempre mais caro do que passar uma referência

Com C ++ 11, nós ganhamos Mover semântica . Em poucas palavras, mover semântica permitir que, em alguns casos, um objeto pode ser passado “por valor” sem copiá-lo. Em particular, este é o caso quando o objeto que você está passando é um rvalue .

Em si mesmo, mover um objeto ainda é pelo menos tão caro como passar por referência. No entanto, em muitos casos, uma função irá copiar internamente um objeto de qualquer maneira -. Ou seja, vai demorar propriedade do argumento 2

Nestas situações, temos o seguinte (simplificado) trade-off:

  1. Podemos passar o objeto por referência, em seguida, copiar internamente.
  2. Podemos passar o objeto por valor.

“Passagem por valor” ainda faz com que o objeto a ser copiado, a menos que o objeto é um rvalue. No caso de um rvalue, o objeto pode ser movido em vez disso, para que o segundo caso é de repente não mais “copy, em seguida, passar”, mas “movimento, em seguida, (potencialmente) se mover novamente”.

Para grandes objetos que implementam construtores movimento adequados (tais como vectores, cordas ...), o segundo caso é então muito mais eficiente do que o primeiro. Portanto, recomenda-se passe uso por valor se a função toma posse do argumento, e se o tipo de objeto suportes em movimento eficiente .


Uma nota histórica:

Na verdade, qualquer compilador moderno deve ser capaz de descobrir quando passar por valor é caro, e implicitamente converter a chamada para usar um const ref, se possível.

Em teoria. Na prática, compiladores nem sempre pode mudar isso sem quebrar interface binária da função. Em alguns casos especiais (quando a função é embutido) a cópia vai realmente ser elidido se o compilador pode descobrir que o objeto original não será alterada por meio das ações na função.

Mas, em geral, o compilador não pode determinar isso, e que o advento de semântica se mover em C ++ fez essa otimização muito menos relevante.


1 Por exemplo em Scott Meyers, Effective C ++ .

2 Isto é especialmente verdadeiro para muitas vezes construtores de objetos, o que pode levar argumentos e armazená-los internamente para ser parte do estado do objeto construído.

Outras dicas

Editar: Novo artigo por Dave Abrahams na CPP de próxima:

Quer velocidade? Passagem por valor.


passagem por valor para estruturas onde a cópia é barato tem a vantagem adicional de que o compilador pode presumir que os objetos não alias (não são os mesmos objetos). Usando passagem por referência o compilador não pode assumir que sempre. Exemplo simples:

foo * f;

void bar(foo g) {
    g.i = 10;
    f->i = 2;
    g.i += 5;
}

o compilador pode otimizar-lo em

g.i = 15;
f->i = 2;

, pois sabe que f e g não compartilha o mesmo local. se G era uma referência (foo &), o compilador não poderia ter assumido que. desde g.i poderia, então, ser alias por f-> i e tem que ter um valor de 7. para que o compilador teria que re-buscar o novo valor de g.i da memória.

Para regras mais práticos, aqui é um bom conjunto de regras encontradas em Mover Construtores artigo ( altamente leitura recomendada).

  • Se a função tem a intenção de alterar o argumento como um efeito colateral, levá-la por referência não-const.
  • Se a função não modifica seu argumento e o argumento é do tipo primitivo, tomá-lo por valor.
  • Caso contrário, levá-la por referência const, exceto nos seguintes casos
    • Se a função, então, necessidade de fazer uma cópia da referência const qualquer maneira, levá-lo por valor.

"Primitive" acima significa, basicamente, pequenas tipos de dados que são alguns bytes de comprimento e não são polimórficos (iterators, objetos de função, etc ...) ou caro para copiar. Nesse papel, há uma outra regra. A idéia é que às vezes se quer fazer uma cópia (caso o argumento não pode ser modificado), e às vezes um não quer (no caso de alguém quiser usar o argumento em si na função se o argumento era de qualquer maneira temporária , por exemplo). O documento explica em detalhes como isso pode ser feito. Em C ++ 1x que a técnica pode ser usado nativamente com suporte ao idioma. Até então, eu iria com as regras acima.

Exemplos: Para fazer uma string para maiúsculas e retornar a versão maiúscula, deve-se sempre passar por valor: Um tem que tomar uma cópia de qualquer maneira (não se podia alterar a referência const diretamente) - então é melhor fazê-lo o mais transparente possível para o chamador e fazer com que copiar cedo para que o chamador pode otimizar o máximo possível - como detalhado em que o papel:

my::string uppercase(my::string s) { /* change s and return it */ }

No entanto, se você não precisa de alterar o parâmetro de qualquer maneira, tomá-lo por referência a const:

bool all_uppercase(my::string const& s) { 
    /* check to see whether any character is uppercase */
}

No entanto, se você o objetivo do parâmetro é para escrever algo para o argumento, em seguida, passá-lo por referência não-const

bool try_parse(T text, my::string &out) {
    /* try to parse, write result into out */
}

Depende do tipo. Você está adicionando a pequena sobrecarga de ter que fazer uma referência e dereference. Para tipos com um tamanho igual ou menor do que os ponteiros que estão usando o ctor cópia padrão, provavelmente seria mais rápido para passar por valor.

Como já foi apontado, isso depende do tipo. Para built-in tipos de dados, é melhor para passar por valor. Até mesmo algumas estruturas muito pequenas, como um par de inteiros pode ter um melhor desempenho, passando por valor.

Aqui está um exemplo, suponha que você tenha um valor inteiro e quer passá-lo para outra rotina. Se esse valor foi otimizado para ser armazenado em um registro, então se você quiser passá-lo ser referência, ele primeiro deve ser armazenado na memória e, em seguida, um ponteiro para que a memória colocado na pilha para executar a chamada. Se ele estava sendo passado por valor, tudo o que é necessário é o registo colocado na pilha. (Os detalhes são um pouco mais complicado do que o indicado sistemas de chamada diferentes e CPUs).

Se você está fazendo programação modelo, que normalmente são forçados a sempre passar por ref const desde que você não conhece os tipos sendo passado. Passando penalidades para passar algo ruim por valor são muito piores do que as penalidades de passar um construídas tipo -in por ref const.

Parece que você tem sua resposta. Passando por valor é caro, mas dá-lhe uma cópia para trabalhar com se você precisar dele.

Isto é o que eu normalmente trabalho por ao projetar a interface de uma função não-template:

  1. passagem por valor se a função não quer modificar o parâmetro eo valor é barato para copiar (int, double, float, char, bool, etc ... Observe que std :: string, std :: vector, eo resto dos recipientes na biblioteca padrão não são)

  2. Passe pelo ponteiro const se o valor é caro para copiar e a função faz não deseja modificar o valor apontado e NULL é um valor que as alças de função.

  3. Passe pelo ponteiro não-const se o valor é caro para copiar e a função quer modificar o valor apontado e NULL é um valor que as alças de função.

  4. passar por referência const quando o valor é caro para copiar e a função não deseja modificar o valor referido e NULL não seria um valor válido se um ponteiro foi usado.

  5. passar por referência não-const quando o valor é caro para copiar e a função quer modificar o valor referido e NULL não seria um valor válido se um ponteiro foi usado.

Como regra passagem por referência const é melhor. Mas se você precisar modificar você funcionar argumento localmente você deve usar melhor passar por valor. Para alguns tipos básicos o desempenho em geral a mesma tanto para passar por valor e por referência. Na verdade reference internamente representada pelo ponteiro, é por isso que você pode esperar, por exemplo, que, para ponteiro tanto de passagem são os mesmos em termos de desempenho, ou até mesmo passando por valor pode ser mais rápido por causa da dereference desnecessária.

Como uma regra de ouro, o valor para tipos não-classe e referência const para as aulas. Se uma classe é realmente pequeno, é provavelmente melhor para passar por valor, mas a diferença é mínima. O que você realmente quer evitar está passando alguma classe gigantesca por valor e ter tudo duplicado -. Isso vai fazer uma enorme diferença se você está passando, digamos, um std :: vector com muito poucos elementos nele

passagem por valor para pequenos tipos.

Passe por referências const para grandes tipos (a definição de grande pode variar entre máquinas), mas em C ++ 11, passagem por valor se você estiver indo para consumir os dados, desde que você pode explorar a semântica movimento. Por exemplo:

class Person {
 public:
  Person(std::string name) : name_(std::move(name)) {}
 private:
  std::string name_;
};

Agora o código de chamada faria:

Person p(std::string("Albert"));

E apenas um objeto seria criado e mudou-se diretamente em name_ membro em Person classe. Se você passar por referência const, uma cópia terá de ser feito para colocá-lo em name_.

diferença simples: - Em função de nós de entrada e parâmetro de saída, por isso, se sua entrada de passagem e fora do parâmetro é o mesmo, em seguida, chamada uso por referência else if entrada e saída parâmetros são diferentes, então melhor chamar de uso pelo valor

exemplo void amount(int account , int deposit , int total )

parâmetro de entrada: conta, depósito paramteter saída: Total

de entrada e saída é diferente chamada uso por vaule

  1. void amount(int total , int deposit )

depósito total de entrada total da produção

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top