Renvoyer * this dans les fonctions membres

J’ai récemment utilisé une bibliothèque qui autorise le type de syntaxe suivant:

MyClass myObject; myObject .setMember1("ssortingng value") .setMember2(4.0f) .setMember3(-1); 

Ceci est évidemment accompli en demandant aux setters de renvoyer un MyClass & type; quelque chose comme retour * ceci. J’aime l’apparence de ce code, mais je ne le vois pas beaucoup. Quand cela se produit, je me demande généralement pourquoi.

Alors, est-ce une mauvaise pratique? Quelles sont certaines des implications de le faire comme ça?

Ceci est parfois appelé idiome de paramètre nommé ou chaînage de méthode. Ce n’est pas une mauvaise pratique, cela peut améliorer la lisibilité. Considérez cet exemple extrait de la FAQ C ++

 File f = OpenFile("foo.txt") .readonly() .createIfNotExist() .appendWhenWriting() .blockSize(1024) .unbuffered() .exclusiveAccess(); 

L’alternative serait d’utiliser des arguments de position dans la méthode OpenFile, en obligeant le programmeur à se souvenir de la position de chaque argument.

Certaines personnes appellent cette programmation fluide (ou une interface fluide). D’autres appellent cela un désordre.

Je me penche un peu vers ce dernier camp Selon mon expérience, dans de nombreux cas, les personnes qui écrivent du code de cette manière dépendent de “l’interface fluide” pour assez d’initialisation d’un object. En d’autres termes, malgré le déguisement, il s’agit toujours d’une initialisation en deux étapes. De même, bien que cela soit probablement évitable dans de nombreux cas, cela semble souvent avoir pour conséquence que beaucoup de ce qui devrait être entièrement privé de la classe est rendu publiquement modifiable via des manipulateurs.

Personnellement, je préfère que les objects soient immuables après la création. Ce n’est clairement pas toujours possible, et dans certains cas, vous ne pouvez même pas vous approcher de très près. Néanmoins, plus vous exposez les objects internes à une manipulation extérieure, moins vous avez la certitude que cet object conserve un état cohérent (et, en général, plus vous avez à faire pour maintenir un état cohérent).

Votre exemple n’est pas l’ idiome des parameters nommés .

Avec les parameters nommés idiom, les traceurs pouvant être chaînés définissent les atsortingbuts d’un pack d’argument (paramètre).

Au lieu de cela, votre code dispose d’un ensemble de parameters permettant de modifier l’object final que vous construisez, appelé construction en deux phases .

En général, la construction en deux phases est simplement Bad ™ et la construction en deux phases est mise en œuvre en exposant les atsortingbuts au code client, comme dans votre exemple, est Very Bad ™.

Par exemple, vous ne voulez généralement pas pouvoir modifier les atsortingbuts d’un object fichier une fois cet object construit (et éventuellement fichier ouvert).

Vive & hth.,

C’est une pratique courante. La surcharge d’ operator= implique de le faire pour chaîner des appels:

 class Foo { public: Foo& operator=(const Foo& f) { if (this != &f) { // check for self-assignment // do some stuff... } return *this; } }; 

Ce code permet de faire des choses comme:

 Foo a, b, c; a = b = c; 

Veuillez noter que la vérification de l’auto-affectation est obligatoire car vous devez souvent désallouer des éléments dans l’object actuel. Autoriser a = a romprait votre code.

Après le commentaire de @Fred Nurk, je voudrais append que vous devriez jeter un coup d’œil à l’idiome Copie-échange pour éviter la duplication de code et émettre un code sans exception.
Consultez les liens ci-dessous pour plus d’informations:

Quel est l’idiome copie-et-swap?
http://gotw.ca/gotw/059.htm

Il n’y a pas de problème avec ce style. Le seul inconvénient est que vous ne pouvez pas utiliser la valeur de retour à des fins plus classiques, telles que renvoyer le résultat de la fonction.

C’est ce qu’on appelle une api fluide . Ce n’est pas une mauvaise pratique, mais un style de programmation différent.

L’inconvénient majeur (IMO) est que, puisque vous renvoyez une référence à vous-même, vous ne pouvez rien renvoyer et il peut être difficile de déboguer des instructions fluides car elles sont considérées par le compilateur comme une “ligne” de code géante.

Je ne sais pas si cela est considéré comme une mauvaise pratique ou non, mais une des implications est que vous ne pouvez plus retourner de code d’erreur. Vous êtes donc obligé d’utiliser des exceptions ou des objects d’erreur laids, transmis par référence. Les exceptions sont parfois la bonne solution, mais ce n’est souvent pas le cas, car elles peuvent être coûteuses à lancer et sont désactivées sur certaines plates-formes.

Je pense vraiment que c’est une question de style, et en raison de la relation étroite qui existe entre le C ++ et la culture et la syntaxe, de nombreux programmeurs C ++ aiment les codes d’erreur et préféreront donc les renvoyer au lieu de renvoyer une référence. Mais j’ai souvent vu ce retour aussi et je ne pense pas que ce soit une mauvaise pratique.

En théorie, vous pourriez vous retrouver avec une référence en suspens si vous faites quelque chose de terrible comme:

 MyClass *myObject = new MyClass; MyClass & dangling = myObject->setMember1("ssortingng"); delete myObject; dangling.setMember2(yrParam); 

Alors soyez conscient de cela.

Ce n’est pas une mauvaise pratique, en fait, vous le voyez souvent avec les stream de sortie afin de concaténer plusieurs chaînes et valeurs.

Le seul inconvénient que je vois est que cela vous empêche de renvoyer quoi que ce soit d’autre, mais si c’est une méthode set, cela ne devrait pas avoir d’importance.