laissez-nous dire que nous avons une fonction du formulaire: clairement Le code ci-dessus a un problème, si la condition échoue, nous posons un problème sur la manière de revenir de cette fonction .
Le creux de ma question est de savoir quelle est la meilleure façon de gérer une telle situation? P> p>
14 Réponses :
Si vous ne vouliez pas modifier le type de retour sur un pointeur, vous pouvez éventuellement utiliser le NULL modèle d'objet p>
Quelle est la durée de vie de 'OurObject' et où est-ce créé? P>
Si vous avez besoin de revenir tôt sans toucher / mettre à jour OurObject et il existe au point où vous retournez, je ne vois pas de problème à y retourner une référence. P>
Si vous essayez de communiquer qu'il y a eu une erreur, vous devrez probablement lancer une exception au lieu de revenir tôt parce que le contrat que votre fonction a convenu de dire qu'elle renvoie une référence à «SomeObject». / p>
Si vous retournez une référence à un temporaire, vous feriez mieux de réparer que em> problème ... p>
+1 pour avertissement sur les temporaires. Cependant, l'objet est membre de la classe somescope de sorte que ce n'est pas un problème. Bien que l'objet existe, dans certains modes, il est inapproprié d'y accéder (d'où la condition de protection).
Ok, essayez simplement de couvrir toutes les bases. J'ai supposé que OurObject était probablement membre. Dans ce cas, si vous ne pouvez pas accéder à l'objet et que vous ne pouvez pas modifier la signature de fonction pour signaler son inaccessibilité par d'autres moyens, je pense que vous êtes bloqué avec une exception à moins que vous ne puissiez créer une deuxième instance de quelque part qui dit essentiellement " Je ne suis pas valide »et renvoyez une référence à cela.
Hmm, c'est une pensée intéressante. Malheureusement, l'objet en question est un conteneur STL, ce qui n'est pas possible.
Serait-il possible d'avoir un conteneur vide flottant à ces fins? Cela signalerait probablement l'utilisateur qu'il n'y ait rien à voir avec les données. Juste une pensée...
euh ... juste jeter une exception ... p>
Semble un peu lourds n'est-ce pas? Sans parler de dangereux car chaque appel devrait être enveloppé dans un bloc d'essai.
Je ne voudrais généralement pas jeter une exception, sauf quelque chose d'inattendu est arrivé. Il y a parfois d'autres bons scénarios où il est logique de lancer, mais cela semble comme si ce n'est pas l'un d'entre eux.
Pourquoi les mains lourdes? Et pourquoi les choses devraient-elles être enveloppées dans un bloc d'essai? Si un contrat de fonction indique que sera i> renvoyer une référence à un objet et que cela ne peut pas alors - presque par définition - c'est une condition exceptionnelle. Une fonction d'appel est libre de faire face à cette exception, mais il n'est pas nécessaire. Il pourrait simplement laisser l'exception propager à une couche capable de le gérer.
En effet, si vous devez envelopper chaque appel dans un bloc d'essai, des exceptions ne sont pas la voie à suivre. Mais vous devriez le signaler dans votre question. La question ne semble pas que somécontion code> est faux est une condition attendue.
Convenait qu'il est lourd, mais lorsque le type de retour est une référence et que vous ne pouvez pas être modifié, vous n'avez vraiment que deux choix: (1) Utilisez le modèle d'objet NULL comme suggéré par yacoby ci-dessus ou (2) lancer une exception. Dans tous les cas, si le type de retour est du tout malléable, j'explore certainement le modifier à un pointeur et passer du temps à justifier pourquoi c'est une référence.
lancer une exception p>
-1: Je ne voudrais généralement pas jeter une exception, sauf quelque chose d'inattendu est arrivé. Il y a parfois d'autres bons scénarios où il est logique de lancer, mais cela semble comme si ce n'est pas l'un d'entre eux.
Non Ceci est un exemple où l'exception est le meilleur choix (à côté de la modification du type de valeur de retour ou de renvoyer une valeur par défaut). Les fonctions qui renvoient référence doivent renvoyer une référence ou lancer une exception.
Adrian: Si lancez une exception n'est pas le meilleur choix que la fonction ne doit pas renvoyer une référence. Demandez-lui que quelque chose d'autre ou transmettez une référence non-const à la fonction à modifier.
J'utiliserais un pointeur au lieu d'une référence dans ce cas. En fait, ce critère (valeur de retour optionnel ou obligatoire) est de savoir comment je décide entre les pointeurs et les références en premier lieu. p>
Je vais envisager de retourner en référence uniquement si je suis sûr que la fonction n'échouera jamais (probablement de renvoyer une référence de const à une variable de membre interne). S'il existe plusieurs validations dans la fonction, je vais considérer l'une des options suivantes: p>
Je retournerais un booléen et passez la référence d'objet comme paramètre sur la fonction: Dans ce cas, votre objet n'a maintenant été rempli que si la fonction retourne vrai. P> p>
Intéressant, je pensais moi-même dans ces lignes.
J'aime cette réponse. Le drapeau vous indique si l'objet transmis a été modifié.
Cela ne fait pas la même chose que le code d'origine, cependant. Le code d'origine renvoie une référence à OurObject (et de modifications futures à OurObject, via des références non-Const, seront visibles via la référence de l'appelant). Cela fait une copie de OurObject, comme au moment où la fonction est appelée. Ni est nécessairement juste ou faux, mais ils ont des objectifs différents.
Ce n'est pas une question syntaxique, mais une question de conception. Vous devez spécifier ce que Selon les problèmes de conception, je vois quelques façons syntaxiques possibles en dehors de ceci: p>
retourObject () code> est censé revenir quand
somécontion code> est vrai. Cela dépend principalement de ce que la fonction va être utilisée. Et que vous ne nous avez pas dit. p>
Somécontion code> est quelque chose d'exceptionnel quels clients ne peuvent pas traiter de ce qui serait approprié li>
Somécontion code> devrait toujours tenir, il devrait être affirmé li>
ul>
Lorsque le C ++ renvoie des références, il s'agit généralement de références implicites aux objets contenus ou au présent pointeur (auto) - simplement à cause de ce problème. Lors du retour des références aux objets, vous devez savoir que vous avez construit un objet valide. Les références sont en quelque sorte inutiles autrement.
Si vous ne voulez pas jeter une exception, vous pouvez essayer quelque chose comme:
const SomeObject& GetSomeObject(const SomeObject& default_if_not_found = SomeObject()) { // ... if (!found) return default_if_not_found; return ourObject; }
Quelle est la durée de vie d'un argument temporaire utilisé comme argument par défaut?
Les temporaires liés à Const-Références ont la même vie que la référence constante qu'ils sont liées. Je suppose que si le temporaire est ensuite retourné, il gagne la durée de vie de la référence de const recevoir le retour de la fonction - ce qui est pratique.
Je crois que la réponse dépend de la question de savoir si vous êtes le genre de programmeur qui considère comme mauvais et ne veut jamais les utiliser ou non. Si vous les utilisez, et c'est vraiment une condition exceptionnelle, une exception doit être lancée. Oui, les utilisateurs doivent y faire face, mais cacher une erreur pour prétendre que rien n'est arrivé n'est tout simplement pas une option. Mais si vous n'utilisez jamais d'exceptions, rendez-vous simplement un pointeur ou un booléen et ajoutez une référence non constituée à votre fonction. Les deux solutions sont simples, mais qui ont dit que les choses trop complexes rendent le code mieux? P>
Je ferais probablement cela: et peut-être aussi: p> Les appelants ont alors quelques options: P> Vous concevez une interface de la fonction différemment en fonction de ce que sont les conditions préalables, c'est-à-dire dont la responsabilité est de s'assurer qu'elle peut faire son travail. P> Si c'est la responsabilité de l'appelant, Ensuite, ils ont besoin de la capacité de s'assurer que cela. Vous voudrez peut-être vérifier quand même, juste pour être du côté sûr. Jeter une exception lorsque les «conditions préalées» ne sont pas remplies les rendent plus comme des conseils que les conditions, et cela peut fonctionner assez bien à condition que cela ne vous dérange pas des frais de contrôle des chèques d'exécution partout. Je suis généralement assez impitoyable avec des conditions préalables, je pourrais donc remplacer l'exception conditionnelle avec une affirmation et documenter que la fonction ne doit pas être appelée en mode incorrect. Cela dépend de la quantité de contrôle de l'appelant sur le mode - évidemment s'il change de manière arbitraire (par exemple, si "la somécontion" est "il n'y a pas d'octet disponible sur le UART"), vous avez besoin d'une interface très différente de si elle ne change que jamais Lorsque le code client appelle une fonction pour le modifier. p> Si ce n'est pas la responsabilité de l'appelant d'obtenir le mode droit, votre interface ne doit pas écrire des chèques que votre implémentation ne peut pas encaisser. Dans ce cas s'il est "attendu" qu'il n'y aura aucun objet de retour, le retour ne devrait pas être par référence. Sauf indication contraire (A), vous pouvez franchir une spéciale "désolé, il n'a pas fonctionné" objet à retourner, que l'appelant peut vérifier, ou (b) vous êtes à l'aise de lancer des exceptions dans les situations "attendues". IMO (B) est rare en C ++. (a) n'est généralement pas mieux que de retourner un pointeur NULL, mais de temps en temps. Par exemple, retourner une collection vide pour signifier "Il n'y a rien que vous êtes autorisé à voir ici" peut entraîner un code d'appel très propre, si DosomeLeelse () code> aurait été "ne rien faire". Donc, ainsi que les responsabilités souhaitées, l'interface dépend des cas d'utilisation attendus. P> p>
+1 Pour une bonne explication et prenant le temps de fournir des exemples clairs.
La manière dont la fonction est configurée, vous devez retourner un si Il y a la possibilité de retourner une sorte d'objet nul. Les pointeurs sont utiles pour cela, car il existe une valeur null évidente qui teste False, mais vous pouvez transmettre un drapeau en tant que paramètre OUT ou renvoyer un objet spécial destiné à une null. Ensuite, vous devrez tester la valeur de retour sur chaque appel pour que cela soit significatif, et ce n'est pas probablement moins tracas que le Si je faisais cela, cela dépend de TooOObject code> ou jeter une exception. Si vous voulez faire autre chose, vous devez modifier la fonction. Il n'y a pas d'autres choix. P>
somécontion code> ne sera jamais faux, vous pouvez supprimer le test dans le code et mettre un
affirmer code> dans ou vous pouvez garder le test et lancer une exception. . (Je suis répugnant de recommander de ne pas en tenir compte de la possibilité, car "jamais fausse" signifie presque toujours "Jamais faux, à moins que quelque chose de mauvais soit arrivé et non détecté", et si cela vaut la peine d'être testé pendant toute condition de détection.) p>
affirmer code> ou
lancer code>. < / p>
Somécontion code>. S'il a des effets secondaires ou n'est pas difficile de calculer, de tester et de lancer une exception si faux. Si c'est onéreuse de calculer, utilisez
affirmer code> de sorte que vous ne devez pas le faire en production et l'oublier à côté de cela. P>
1. em> strong> lancer une exception
2. em> strong> retourne un objet d'erreur factice
3. em> strong> Utilisez un pointeur et retournez null p>
Ceci est la même situation que l'opérateur C ++ intégré dynamic_cast <> code>. Il peut être coulé à un type de pointeur ou à un type de référence. Lorsqu'il s'agit d'un type de pointeur, il renvoie
null code> sur une défaillance. Lorsqu'il s'agit d'un type de référence, il jette l'exception
std :: bad_cast code> sur l'échec. Suite au même exemple, vous lanceriez une exception. P>
Je pense que cela aiderait à savoir quel type de condition vous parlez. Cela pourrait aider à décider si une exception serait ou non une exception serait une option valide.
La classe dispose de deux ensembles de membres différents et dispose de deux "modes". Retourbject est simplement une vérification des accesseurs et de la somécontion pour voir si le membre qui est demandé est approprié pour le mode donné.
@Whyam, sonne comme cette condition étant fausse indique une erreur logique dans votre code. Donc, ce serait un cas pour
affirmer code>.
@LitB, la somécontion affirme et enregistre si la condition échoue. Cependant, cela ne répond pas à ma question.
Affirmer n'est pas une option. Vous devez lancer ou renvoyer un type nullable (pointeur E.g).
Pourquoi affirmerait-il ne pas être une option? Si une condition préalable est cassée, affirmer est un bon outil pour vous en dire à ce sujet. Si le chemin contenant l'assert n'est pas pris, il n'y a pas de mal. Si le chemin est pris, il ne sert à rien de poursuivre l'exécution du programme.
@Whyamistilltyping: Si vous utilisez une affirmation, cela vous semble que vous (A) devrait être et (B) le faire déjà, vous n'avez pas besoin de renvoyer quoi que ce soit. Vous n'avez pas besoin de ce
si (! Somécontion) code> du tout - il suffit de remplacer tout le bloc complet avec
affirmation (somécontion); code>.
Affirmer n'est pas une option car il doit toujours retourner quelque chose / lancer et car il est supprimé dans la version de version. J'affirme comme ce
si (! Cond) {ASSERT (! "Message"); / * Faites quelque chose de concret * /} code> Assert est une aide de débogage, il ne doit pas être utilisé pour la manipulation des erreurs.
assert code> est un outil qui vous aide à saisir des erreurs de programmation pendant les tests. Si
Somécontion code> doit toujours contenir dans un logiciel sans bug, alors
! Somécontion code> doit être affirmé afin que les bugs soient trouvés lors de tests. Si cela pourrait ne pas contenir dans un logiciel sans bug, il ne doit pas être appliqué par un
affirmer code>, mais par d'autres moyens.
Vous ne pouvez pas écrire quelque chose d'illégal juste en mettant un
affirmer code> devant celui-ci. La succursale code>! Somécontion code> doit renvoyer quelque chose de compatible avec le type de retour de la fonction ou lancer une exception.
@David, il n'y a rien d'illégal sur
int a [1]; int & f (int n) {assert (n == 0); retourner un [n]; } ... f (1); code> je pense. En mode de débogage, il se plaindre et en mode libération, l'appelant gagnera le comportement non défini qu'il a demandé :) La question dépend également de la manière dont le code est utilisé. Est-ce utilisé comme bibliothèque? (Je ne pense pas que les bâtonnets de mise en œuvre teste dans
opérateur [] code> de
std :: vecteur code> en mode de version). Ou est-ce que vous savez comment il est utilisé et où les tests en mode de sortie vont bien?
@Johannes, si vous mettez une affirmation pour vérifier si n == 0, vous devez ajouter un test sur n parce que vous savez que c'est un cas d'échec (même si vous ne devriez jamais l'avoir).
Je pense aller avec les termes définis dans blogs.msdn. COM / ERICLIPPERT / Archive / 2008/09/10 / ... , c'est une "exception osseuse" :) on ne l'attrape jamais. Donc, pour moi, cela ressemble à on doit abandonner l'exécution tout de suite au lieu de vous déranger avec le déroulement.
Je pense que c'est aussi la peine de noter les différentes capacités des langues. Où
java code> jette une exception dérivée de
erreur code>, et peut vous montrer une belle trace de pile, pour C ++ si vous laissez l'exception propager il est indéterminé si la pile est réellement déroutée et non spécifiée. Vous risquez de ne pas savoir où l'exception s'est propagée. Pour cette raison, je joue "STOP EXECUTION MAINTENANT, puis BackTrace avec le débogueur" -game.
LitB: Et il y a toujours la possibilité d'utiliser votre propre
myASsert code> macro. Habituellement, c'est juste
affirmer code>, mais lorsque le débogage devient méchant, vous le réécrivez pour piéger dans le débogueur, lancez une exception, tracez une partie de l'état actuel, ou tout ce qui pourrait aider. La comparaison avec Java est APT, je pense - certains programmeurs de niveau supérieur, ne connaissent tout simplement pas la signification des mots ", vous ne devez pas appeler cette fonction à moins que" et vraiment ressentir leurs libertés civiles violées par des fonctions qui ne font que tomber un indice hors limites ou autre chose. Vous devez concevoir votre public attendu ;-)
@David: "Vous ne pouvez pas écrire quelque chose d'illégal simplement en mettant une affirmation devant elle". Non, mais vous pouvez écrire quelque chose d'illégal en documentant ", cette fonction ne doit pas être appelée si ce qui suit serait illégal". Donc, par exemple
SHLEN code> DERREFS son argument sans vérifier pour NULL, et il n'y a rien de mal à cela. Si vous ajoutez également une affirmation, alors franchement, c'est un acte héroïque de la charité envers votre appelant (sans oublier un mouvement intelligent, en considérant que l'appelant est probablement vous et que vous souhaitez trouver vos propres bugs).