Pergunta

Enquanto refatoração algum código me deparei com alguns métodos getter que retorna um std :: string. Algo como este, por exemplo:

class foo
{
private:
    std::string name_;
public:
    std::string name()
    {
        return name_;
    }
};

Certamente o getter seria melhor retornar um const std::string&? O método atual está retornando uma cópia que não é tão eficiente. Será que retornar uma referência const em vez causar algum problema?

Foi útil?

Solução

A única maneira isso pode causar um problema é se as lojas de chamada que o de referência, ao invés de copiar a cadeia e tenta usá-lo depois que o objeto é destruído. Como esta:

foo *pFoo = new foo;
const std::string &myName = pFoo->getName();
delete pFoo;
cout << myName;  // error! dangling reference

No entanto, desde a sua função existente retorna uma cópia, então você não quebrar nada do código existente.

Outras dicas

Na verdade, uma outra questão especialmente com a devolver uma cadeia não por referência, é o fato de que std::string fornece acesso via ponteiro para um const char* interno através da c_str () método . Isso tem me causado muitas horas de depuração dor de cabeça. Por exemplo, digamos que eu quero para obter o nome de foo, e passá-lo para JNI a ser usado para construir um jstring para passar para Java mais tarde, e que name() está retornando uma cópia e não uma referência. Eu poderia escrever algo como isto:

foo myFoo = getFoo(); // Get the foo from somewhere.
const char* fooCName = foo.name().c_str(); // Woops!  foo.name() creates a temporary that's destructed as soon as this line executes!
jniEnv->NewStringUTF(fooCName);  // No good, fooCName was released when the temporary was deleted.

Se o seu interlocutor vai estar fazendo esse tipo de coisa, talvez seja melhor usar algum tipo de ponteiro inteligente, ou uma referência const, ou pelo menos ter um comentário aviso cabeçalho desagradável sobre sua foo.name ( ) método. Menciono JNI porque ex-programadores Java podem ser particularmente vulneráveis ??a este tipo de método de encadeamento que podem parecer inofensivos.

Um problema para o retorno de referência const seria se o usuário codificado algo como:

const std::string & str = myObject.getSomeString() ;

Com um retorno std::string, o objeto temporária permaneceria vivo e anexado ao str até str sai do escopo.

Mas o que acontece com um const std::string &? Meu palpite é que teríamos uma referência const para um objeto que poderia morrer quando seu objeto pai desaloca-lo:

MyObject * myObject = new MyObject("My String") ;
const std::string & str = myObject->getSomeString() ;
delete myObject ;
// Use str... which references a destroyed object.

Assim, a minha preferência vai para o retorno de referência const (porque, de qualquer maneira, eu sou apenas mais confortável com o envio de uma referência do que esperando que o compilador irá otimizar o temporária adicional), enquanto o seguinte contrato é respeitado: "se você quer além da existência do meu objeto, que copiá-lo antes da destruição do meu objeto "

Algumas implementações de memória compartilhada std :: string com a semântica copy-on-write, por isso retorno por valor pode ser quase tão eficiente quanto o retorno por referência e você não tem se preocupar com as questões da vida (o tempo de execução faz isso por você).

Se você está preocupado com o desempenho, em seguida, benchmark (<= não posso salientar que o suficiente) !!! Experimente ambas as abordagens e medir o ganho (ou falta dela). Se um é melhor e você realmente se importa, então usá-lo. Se não, então preferem por valor para a proteção que ele oferece agains questões da vida mencionados por outras pessoas.

Você sabe o que dizem sobre como fazer suposições ...

Ok, então as diferenças entre retornando uma cópia e devolver a referência são:

  • Desempenho : Retornando a referência pode ou não pode ser mais rápido; isso depende de como std::string é implementado por sua implementação compilador (como outros têm apontado). Mas mesmo se você retornar a referência a atribuição após a chamada de função geralmente envolve uma cópia, como em std::string name = obj.name();

  • Segurança : Retornando a referência pode ou não pode causar problemas (referência pendurado). Se os usuários de sua função não sei o que eles estão fazendo, armazenando a referência como referência e usá-lo depois que o objeto fornecendo sai do escopo, então há um problema.

Se você quer que ele rápida e segura uso boost :: shared_ptr . Seu objeto pode armazenar internamente a string como shared_ptr e retornar um shared_ptr. Dessa forma, não haverá cópia do objeto indo e e é sempre mais seguro (a menos que seus usuários retire o ponteiro bruto com get() e fazer coisas com ele após o seu objeto sai do escopo).

Eu mudá-lo para retornar std :: string const &. O chamador provavelmente irá fazer uma cópia do resultado de qualquer maneira, se você não mudar tudo o código de chamada, mas não irá apresentar quaisquer problemas.

Uma ruga potencial surge quando você tem vários segmentos chamando o nome (). Se você retornar uma referência, mas, posteriormente, alterar o valor subjacente, então o valor do chamador vai mudar. Mas o código existente não parece de qualquer maneira thread-safe.

Dê uma olhada na resposta de Dima para um problema potencial, mas-improvável relacionado.

É concebível que você poderia quebrar alguma coisa se o chamador realmente queria uma cópia, porque eles estavam prestes a alterar o original e queria preservar uma cópia do mesmo. No entanto, é muito mais provável que ele deve, de fato, ser apenas retornando uma referência const.

A melhor coisa a fazer é experimentá-lo e, em seguida, testá-lo para ver se ele ainda funciona, desde que você tenha algum tipo de teste que você pode executar. Se não, eu concentrar em escrever o teste primeiro, antes de continuar com refatoração.

Será que isso importa? Assim que você usar um compilador otimizado moderno, funções que retornam por valor não vai envolver uma cópia a menos que sejam semanticamente obrigados a.

o C ++ Lite FAQ neste .

As probabilidades são boas bonita que uso típico dessa função não vai quebrar se você mudar para uma referência const.

Se todo o código chamar essa função está sob seu controle, basta fazer a mudança e ver se o compilador reclama.

Depende do que você precisa fazer. Talvez você quer toda a chamada para alterar o valor retornado sem alterar a classe. Se você retornar a referência const que não vai voar.

Claro, o próximo argumento é que o chamador poderia, então, fazer a sua própria cópia. Mas se você sabe como a função será usado e saber o que acontece de qualquer maneira, então talvez fazendo isso poupa-lhe uma etapa posterior no código.

Eu normalmente retornar const & a menos que eu não posso. QBziZ dá um exemplo de onde este é o caso. Claro QBziZ também afirma que std :: string tem semântica copy-on-write, que raramente é verdade hoje, já que COW envolve um monte de sobrecarga em um ambiente multi-threaded. Ao retornar const & você coloca o ônus sobre o chamador para fazer a coisa certa com a corda em sua extremidade. Mas desde que você está lidando com o código que já está em uso, você provavelmente não deve alterá-lo, a menos de perfil mostra que a cópia desta cadeia está causando problemas de desempenho maciças. Então, se você decidir alterá-lo você precisará testar thouroughly para se certificar de que você não quebrar nada. Esperemos que os outros desenvolvedores que trabalham com você não fazer coisas esboçado como na resposta de Dima.

Retornando uma referência a um membro expõe a implementação da classe. Aquela poderia evitar alterar a classe. Pode ser útil para métodos privados ou protegidos meter a otimização é necessário. O que deve um C ++ retorno getter

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