Pergunta

Eu estou tentando criar am tipo imutável (classe) em C ++,

Eu fiz isso para que todos os métodos "aka funções membro" não modificar o objeto e retornar uma nova instância vez.

Estou correndo através de um monte de problemas, mas todos eles giram em torno dos tipos de referência em C ++.

Um exemplo é quando passar parâmetros do mesmo tipo de classe por referência:

Imm Imm::someOp( const Imm& p_im ) const
{
   ...
   p_im = p_im.someOtherOp(); //error, p_im is const, can't modify it!
   ...
}

O erro é causado pela passagem do valor de referência. Se em vez disso, eu estava passando a referência de valor, então a linha de erro acima não seria um erro!

Considere um exemplo # Java / C

class Imm
{
    ...
    Imm someOp( Imm p_im )
    {
        ....
        p_im = p_im.someOtherOp(); //ok, you're not modifying the 
                 //original object, just changing the local reference
        ....  
    }
    ....
}

Como posso fazer algo parecido com isso em C ++? Eu sei que posso usar ponteiros, mas depois eu me deparo com toda a bagunça de gerenciamento de memória. Eu não quero que se preocupar com quem possui referências a objetos.

Idealmente, eu gostaria de projetar a classe a ser como cordas imutáveis ??em python; você pode usá-los sem nunca perceber ou até mesmo sabendo que eles são imutáveis, e eles apenas se comportam como você espera; eles só trabalho.

Editar

Claro que posso obter em torno dele, passando por valor ou usando uma variável temporário (que é o que estou fazendo atualmente). O que estou pedindo é sobre "como passar referências por valor em C ++"

Eu estou esperando a resposta para girar em torno de algo no STL, estou investigando smart_ptr família de modelos.

Atualizar

Obrigado pelas respostas, eu percebo que não há fuga de ponteiros. (Ver o meu outra questão, que é realmente um acompanhamento sobre este)

Foi útil?

Solução

Em Java e C #, você não está realmente lidando com uma referência - eles são mais como alças ou ponteiros. Uma referência em C ++ é realmente um outro nome para o objecto original, não um apontador para ele (embora possa ser implementado com um ponteiro). Quando você atribuir um valor a uma referência, você está atribuindo ao próprio objeto. Há uma confusão em que inicializar uma referência que você pode usar o caractere =, mas é uma inicialização, não uma atribuição.

 Imm im, im2, im3; 
 Imm &imr = im;  // initialize a reference to im
 imr = im2; // assign im2 to imr (changes the value of im as well)
 Imm *imp = &im; // initialize a pointer to the address of im
 imp = &im3; // assign the address of im3 to imp (im is unnaffected);
 (*imp) = im2; // assign im2 to imp (modifies im3 as well).

Se você quer especificamente para passar "referências por valor", então você está basicamente pedindo uma contradição em termos. As referências, por definição, são passados ??por referência. Como apontado em outro lugar, você pode passar um ponteiro por valor, ou então um valor em linha reta. Se você realmente quiser, você pode agarrar uma referência em uma classe e passar essa volta por valor:

 struct ImmRef
 {
     Imm &Ref;
     ImmRef(Imm &ref) : Ref(ref) {}
 };

Note-se também que uma const aplicada a uma referência está fazendo o referido objeto constante, e não o de referência. Referências são sempre const.

Outras dicas

Não é atribuição, por definição, não uma operação constante?

Parece que você está tentando algo atribuir a uma referência const, que anula totalmente a idéia de uma referência const.

Eu acho que você pode estar procurando por um ponteiro em vez de uma referência.

Não funciona assim em C ++.

Quando você passar uma referência a um objeto, você está realmente passando o endereço na memória do objeto. As referências não podem ser a outros objectos quer, por conseguinte, a ++ ditado C sentam-re "A referência é o objecto." Você tem que fazer uma cópia para modificá-lo. Java vai fazer isso por trás dos bastidores para você. C ++, você só tem que copiá-lo.

Você não se esqueça de definir o método que você chama como const?

EDIT:. Assim, com a const fixa

Talvez você deve fazer algo como

Imm & tmp = p_im.someOtherOp();

Em seguida, faça outra operação sobre a variável tmp.

Se você definir uma variável ou um parâmetro como const & você simplesmente não pode atribuir a ele.

Verifique isso para saber sobre vida temporária http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

Imm Imm::someOp( const Imm& p_im ) const
{
   ...
   //Imm& im = p_im.someOtherOp();       // will *not* work
   const Imm& im = p_im.someOtherOp();   // will work, but you get a const reference
   ...
}

Mas você pode usar boost :: shared_ptr

shared_ptr<Imm> Imm::someOtherOp() const
{
  shared_ptr<Imm> ret = new Imm;
  ...
  return ret;
}

shared_ptr<Imm> Imm::someOp(const share_ptr<Imm>& p_im) const
{
  shared_ptr<Imm> im = p_im->someOtherOp();
}

Você precisa fazer uma nova cópia de seu argumento de entrada. Você pode fazer o que quiser de várias maneiras equivalentes: 1) você poderia passar por valor:

Imm Imm::someOp( Imm im ) const {
   im = im.someOtherOp();      // local im is a copy, original im not modified
   return im;                  // return by value (another copy)
}

ou 2) você pode passar por referência e faça uma cópia explicitamente:

Imm Imm::someOp( const Imm & im ) const {
   Imm tmp = im.someOtherOp(); // local tmp is a copy
   return tmp;                 // return by value (another copy)
}

ambas as formas são equivalentes.

C ++ tem algo melhor do que imutável tipos-const. Um único tipo pode ser mutável ou não dependendo de suas necessidades. Dito isto, existem padrões úteis para lidar com a curto tempo de vida (quase-) cópias:

void f(const X &x) {
  // Trivial case: unconditional copy
  X x2=transform(x);
  // Less trivial: conditional copy
  std::optional<X> maybe;
  const X &use=need_copy ? maybe.emplace(transform(x)) : x;
  use.go();  // whichever, x or *maybe
}  // *maybe destroyed iff created, then x2 destroyed

std::unique_ptr pode ser utilizado de forma semelhante antes da C ++ 17, embora a função possa, em seguida, de std::bad_alloc curso arremesso.

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