Question

J'essaie de créer un type immuable (classe) en C ++,

J'ai fait en sorte que toutes les méthodes "fonctions de membre aka"; ne modifiez pas l'objet et ne retournez pas une nouvelle instance.

Je rencontre plusieurs problèmes, mais ils tournent tous autour des types de référence en C ++.

Un exemple en est le passage de paramètres du même type de classe par référence:

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

L'erreur est provoquée par le passage de la valeur par référence. Si au lieu de cela, je passais la référence par valeur, la ligne d'erreur ci-dessus ne serait pas une erreur!

Considérons un exemple 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
        ....  
    }
    ....
}

Comment puis-je faire quelque chose comme ça en C ++? Je sais que je peux utiliser des pointeurs, mais je me heurte à tout le désordre de la gestion de la mémoire. Je ne veux pas m'inquiéter de savoir à qui appartiennent les références aux objets.

Idéalement, j'aimerais que la classe ressemble à des chaînes immuables en python; vous pouvez les utiliser sans jamais vous rendre compte, ni même savoir qu'ils sont immuables, et ils se comportent simplement comme vous le souhaitez; ils travaillent juste.

MODIFIER

Bien sûr, je peux le contourner en passant valeur par valeur ou en utilisant une variable temporaire (ce que je suis en train de faire actuellement). Ce que je demande, c'est "comment passer des références par valeur en C ++"

Je m'attends à ce que la réponse tourne autour de quelque chose dans la STL, je suis en train d'examiner la famille de modèles smart_ptr.

MISE À JOUR

Merci pour les réponses, je me rends compte que rien ne peut échapper aux pointeurs. (voir mon autre question , ce qui est vraiment un suivi de celle-ci)

Était-ce utile?

La solution

En Java et en C #, vous n’avez pas vraiment affaire à une référence, c’est plutôt à des poignées ou des pointeurs. Une référence en C ++ est en réalité un autre nom pour l'objet d'origine et non un pointeur sur celui-ci (bien qu'il puisse être implémenté avec un pointeur). Lorsque vous affectez une valeur à une référence, vous affectez à l'objet lui-même. Il existe une confusion en ce que pour initialiser une référence, vous pouvez utiliser le caractère = , mais il s'agit d'une initialisation, pas d'une affectation.

 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).

Si vous souhaitez spécifiquement transmettre les " références par valeur " alors vous demandez essentiellement une contradiction dans les termes. Les références, par définition, sont passées par référence. Comme indiqué ailleurs, vous pouvez passer un pointeur par valeur ou une valeur simple. Si vous le souhaitez vraiment, vous pouvez conserver une référence dans une classe et la transmettre par valeur:

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

Notez également qu'un const appliqué à une référence rend l'objet référencé constant, et non la référence. Les références sont toujours const.

Autres conseils

L’affectation n’est-elle pas, par définition, pas une opération constante?

Vous semblez vouloir attribuer quelque chose à une référence const, ce qui annule totalement l'idée d'une référence const.

Je pense que vous recherchez peut-être un pointeur au lieu d'une référence.

Cela ne fonctionne pas comme ça en C ++.

Lorsque vous transmettez une référence à un objet, vous transmettez en réalité l'adresse en mémoire de l'objet. Les références ne peuvent pas non plus être réaffectées à d'autres objets, d'où l'adage C ++ "la référence EST l'objet". Vous devez faire une copie pour le modifier. Java le fera en coulisse pour vous. C ++, il vous suffit de le copier.

N'avez-vous pas oublié de définir la méthode que vous appelez comme const?

EDIT: donc, avec le const fixe.

Peut-être devriez-vous faire quelque chose comme

Imm & tmp = p_im.someOtherOp();

Ensuite, effectuez d'autres opérations sur la variable tmp.

Si vous définissez une variable ou un paramètre comme étant const & amp; vous ne pouvez simplement pas lui attribuer.

Cochez cette case pour connaître la durée de vie temporaire 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
   ...
}

Mais vous pouvez utiliser 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();
}

Vous devez créer une nouvelle copie de votre argument entrant. Vous pouvez faire ce que vous voulez de plusieurs manières équivalentes: 1) vous pouvez passer par valeur:

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) vous pouvez passer par référence et en faire une copie de manière explicite:

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

les deux formes sont équivalentes.

C ++ a quelque chose de mieux que les types immuables & # 8212; const . Un seul type peut être modifiable ou non en fonction de vos besoins. Cela dit, il existe des modèles utiles pour traiter les copies (quasi) de courte durée de vie:

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 peut être utilisé de la même manière avant C ++ 17, bien que la fonction puisse ensuite bien sûr lancer std :: bad_alloc .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top