Alguma diferença entre f (const string &) e f (string const)?
-
21-09-2019 - |
Pergunta
class mystring {
friend ostream& operator<<(ostream &out, const mystring ss) {
out << ss.s;
return out;
}
private:
string s;
public:
mystring(const char ss[]) {
cout << "constructing mystring : " << ss << endl;
s = ss;
}
};
void outputStringByRef(const mystring &ss) {
cout << "outputString(const string& ) " << ss << endl;
}
void outputStringByVal(const mystring ss) {
cout << "outputString(const string ) " << ss << endl;
}
int main(void) {
outputStringByRef("string by reference");
outputStringByVal("string by value");
outputStringByRef(mystring("string by reference explict call mystring consructor"));
outputStringByVal(mystring("string by value explict call mystring constructor"));
} ///:~
Considerando o exemplo acima, não conseguimos modificar a variável de passagem por referência, também não poderíamos modificar a variável de passagem por valor. A saída de cada métodos é a mesma. Como não há diferença entre esses dois métodos, por que C ++ Suportar os dois métodos?
obrigado.
Solução
Há uma diferença entre os dois. Considere o seguinte:
#include <iostream>
#include <string>
using std::string;
string g_value;
void callback() {
g_value = "blue";
}
void ProcessStringByRef(const string &s) {
callback();
std::cout << s << "\n";
}
void ProcessStringByValue(const string s) {
callback();
std::cout << s << "\n";
}
int main() {
g_value = "red";
ProcessStringByValue(g_value);
g_value = "red";
ProcessStringByRef(g_value);
}
Resultado:
red
blue
Só porque uma referência é const em uma função, não significa que o referente não possa ser modificado por outras referências (a situação de um objeto com várias referências ou ponteiros é chamado de "aliasing"). Portanto, existe uma diferente entre a passagem de uma referência const e a aprovação de um valor const - no caso da referência, o objeto pode mudar após a chamada. No caso do valor, o Callee possui uma cópia privada, que não será alterada.
Como eles fazem coisas diferentes, o C ++ permite escolher o que você deseja.
De qualquer forma, existem consequências para o desempenho - quando você passa por valor, uma cópia deve ser feita, quais custos. Mas o compilador sabe então que apenas sua função pode ter referências a essa cópia, o que pode permitir outras otimizações. ProcessStringByref não pode carregar o conteúdo da string para imprimir até callback()
Voltou. ProcessStringByValue pode, se o compilador achar que isso é mais rápido.
Geralmente você se preocupa com a cópia, não com a ordem de execução de instruções, porque geralmente a cópia é muito mais cara. Normalmente, você passa por referência sempre que possível para objetos que não são triviais para copiar. Mas a possibilidade de alias às vezes tem consequências realmente sérias para o desempenho, impedindo certas otimizações, mesmo que nenhum alias realmente ocorra. É por isso que "regras estritas de alias" existem, e o restrict
Palavra -chave em C99.
Outras dicas
f(const string&)
pega string by const
referência: f
está operando diretamente no objeto String passado por referência: não há cópia envolvida. const
Evita modificações no objeto original.
f(const string)
leva um valor de string, o que significa f
recebe uma cópia da string original. Mesmo se você cair const
, ao passar por valor, qualquer modificação para a string é perdida quando f
retorna.
Não sei exatamente o que você quer dizer com "Por que o C ++ suporta os dois métodos?". São apenas regras gerais de sobrecarga que se aplicam.
f(string s)
Passe pelo valor da string s, em outras palavras, cria uma cópia e a inicializa com o valor da string que você passa. Qualquer alteração na cópia não será propagada para a string original que você passou para chamar a função. Dentro f(const string s)
Const é redundante porque você não pode alterar o valor original.
Dentro f(const string& s)
Em vez disso, a string não é copiada, mas você passa uma referência a ela. Isso geralmente é feito quando você tem um objeto grande para que o "valor por valor" possa produzir uma sobrecarga (é por isso que o C ++ suporta os dois métodos). Passar por referência significa que você pode alterar o valor do objeto "grande" que você passa, mas, devido ao especificador const, você não pode modificá -lo. É uma espécie de "proteção".
Seu objeto pode conter um mutável membro, que pode ser modificado mesmo com uma referência const
Com outputStringByRef
você deve garantir que a variável a que está passando permanece viva e inalterada pelo tempo que outputStringByRef
precisa disso. com outputStringByVal
A variável que você passou pode morrer ou sair do escopo e a cópia que a função tem ainda está bem.
(Quase) não há diferença em termos de poder modificar a string dentro da função. No entanto, há uma grande diferença em termos do que está sendo aprovado. o const mystring &ss
Sobrecarga requer uma referência const para a string. Embora não possa modificá -lo, é a mesma memória que está sendo abordada. Se a string for longa, isso pode ser um grande fator (assumindo que a string não seja implementada usando cópia-em-escrever). o const mystring ss
O formulário está fazendo uma cópia da string, para que a memória diferente seja abordada.
Na verdade const mystring &ss
Formato poderia Altere a string, se um const_cast<mystring&>
foi usado - embora eu não recomendaria isso aqui.
Imagine que você deseja imprimir um objeto que não possa ser copiado:
Thread th;
// ...
cout << th; // print out informations...
A versão de referência não copiará o tópico, mas levará o endereço de th
e crie um pseudônimo para ele. A outra versão tentaria copiar o thread ao passar por valor - mas a cópia pode não ser sentida para esse objeto (haveria um thread adicional então?).
Pode ajudá -lo a pensar em copie um objeto Como clonando um objeto. Cópia th
Acima em C ++, não significa apenas ter outra alça para o mesmo objeto que em Java - significa clonar implicitamente o objeto denotado e ter uma cópia inteira dele. Portanto, a pergunta é semelhante a "Por que Java suporta ambos Object.clone
e cópia de referências? " - ambos têm propósitos diferentes.
E também há uma questão de desempenho, afinal. Você não deseja copiar sempre que passa algo para obter objetos com fome de recursos.