J'ai une question sur l'utilisation de l'instruction GOTO en C ++. Je crois comprendre que ce sujet est controversé et je ne suis pas intéressé par des conseils ou des arguments radicaux (je m'empêche généralement d'utiliser goto code>). J'ai plutôt une situation spécifique et je veux comprendre si ma solution, qui utilise la déclaration GOTO, est une bonne ou non. Je ne m'appellerais pas nouveau à C ++, mais ne me classerais pas non plus comme un programmeur de niveau professionnel. La partie du code qui a généré ma question tourne dans une boucle infinie une fois commencé. Le débit général du fil dans le pseudocode est le suivant:
void ControlLoop::main_loop()
{
InitializeAndCheckHardware(pHardware) //pHardware is a pointer given from outside
//The main loop
while (m_bIsRunning)
{
simulated_time += time_increment; //this will probably be += 0.001 seconds
ReadSensorData();
if (data_is_bad) {
m_bIsRunning = false;
goto loop_end;
}
ApplyFilterToData();
ComputeControllerOutput();
SendOutputToHardware();
ProcessPendingEvents();
while ( GetWallClockTime() < simulated_time ) {}
if ( end_condition_is_satisified ) m_bIsRunning = false;
}
loop_end:
DeInitializeHardware(pHardware);
}
7 Réponses :
Éviter l'utilisation de Dans votre cas, pourquoi pas simplement utiliser Tant que votre question: Votre utilisation de en particulier Je ne suis tout simplement pas d'accord que p> est plus lisible que p> qui dit littéralement la même chose avec en syntaxe. P> p> goto code> est une chose assez solide à faire dans le développement orienté objet en général.
Break code> pour quitter La boucle? p>
goto code> me ferait mal à l'aise. La seule raison pour laquelle
break code> est moins lisible est votre admission à ne pas être un développeur C ++ fort. À tout développeur assaisonné d'une langue similaire,
pause code> va à la fois mieux, ainsi que de fournir une solution plus propre que
goto code>. P>
Ma principale préoccupation était que la principale boucle du code réel est assez longue, et je pense donc que l'utilisation de la pause en fait un peu moins claire lorsque le flux de programme ira à la lecture immédiate. Merci, cependant, pour votre commentaire. Cela semble être un point de vue raisonnable.
Tant que vous n'avez pas d'espacement étrange dans votre code, votre code doit alors s'écouler de manière assez lanière à l'aide du pendant {} code> avec tout développeur expérimenté. Il est particulièrement facile de suivre moins de la boucle.
En outre, il est possible qu'un IDE vous indique exactement où le code> code> ira (bien que je ne sache pas s'il existe des idées qui ont cette fonctionnalité particulière).
@Hunter: Mon principale préoccupation était que la boucle principale dans le code réel est assez longue i> => Lorsqu'une fonction est trop longue pour s'adapter confortablement sur votre écran, il est temps de commencer à partir de morceaux de factorisation en plus petites fonctions; Que vous utilisiez goto code> ou
break code> à ce stade est hors de propos.
@Matthieu merci. Je suis d'accord avec cette notion. Dans des cas isolés, cependant, je trouve cela assez énervant de facturer des morceaux. Je ne dis pas que tu as tort, ça peut être ennuyeux. Lorsque les différentes parties d'une séquence longue partagent beaucoup d'informations contextuelles, les fonctions doivent avoir de nombreux arguments ou devraient partager de nombreuses données en modifiant les membres de la classe, ce qui pourrait (quelque peu) cacher la relation de causalité entre les pièces. Ou, un type d'objet supplémentaire, principalement sans signification, est créé pour mélanger les données.
au lieu d'utiliser goto code>, vous devez utiliser
break; code> pour échapper aux boucles. p>
Pour un seul niveau de boucle, c'est vrai. Qu'en est-il de plusieurs niveaux de boucle?
L'affiche utilisait GOTO pour sortir de cette seule boucle, donc la pause est la meilleure option (IMO) dans le cas de l'OP.
D'accord; Vous avez répondu à la lettre de la question. Mais les réponses qui vont au-delà de la lettre de la question et répondent à l'esprit de la question sont plus susceptibles d'être éliminées. Le problème n'est pas que goto code> est automatiquement mauvais; Ce n'est pas correct dans l'exemple, mais il n'est pas difficile de concevoir des circonstances dans lesquelles il est approprié.
Si votre principale préoccupation est la boucle tandis que la boucle est trop longue, vous devriez viser à le rendre plus court, C ++ est une langue oo et oo est pour des choses divisées aux petites pièces et Composant, même en général non-Oo, nous pensons généralement que nous devrions briser une méthode / boucle en petite et la rendre courte facile à lire. Si une boucle ait 300 lignes dedans, aucune rupture / goto ne sauvegardez pas vraiment votre séjour là-bas n'est pas? P> Mise à jour forte> p> i Je ne suis pas contre La chose que je peux imaginer que j'utilise un goto serait de sortir d'une boucle de deux niveaux: p>
Merci, Simon. La boucle principale n'est pas ridiculement longue (~ 150 lignes), mais est plus longue qu'un écran, du moins pour moi. Je pourrais certainement le casser plus. J'ai déjà fait cela dans le degré que j'ai pensé raisonnable, cependant. Cela peut appartenir à une autre question, et si vous n'hésitez pas à ne pas répondre, mais que vous feriez de telles méthodes de classe de sous-routines qui communiquent en stockant des données dans la classe ou fonctionnent simplement dans la même unité de traduction qui prennent des arguments pour la sortie.
Eh bien, cela dépend vraiment, et je ne sais pas ce que je ferais jusqu'à ce que je le fasse;) la règle que je suivrais surtout ferait que chaque classe se concentre sur une zone avec juste faire une chose, mais personne n'est parfait et je pourrais fait du codage laid que je ne peux pas au courant. Je dirais ici laissez le code tel qu'il est là si vous et votre équipe se sentez tous bien avec ce goto et personne ne veulent le supprimer.
Avec votre une, une condition singulière qui provoque la pause de la boucle tôt, j'utiliserais simplement une pause Cependant, si tout em> de ces appels de fonction peut lancer une exception ou si vous finir par avoir besoin de multiples code>. Pas besoin d'un
goto code> c'est ce que
pause code> est pour.
pause code> S Je préférerais un conteneur de style Raii, il s'agit du type de chose exact destructeurs. Vous effectuez toujours l'appel à
DEINITIALIZEHARDWARDAWARDWARDWARDAWARD CODE>, SO ... P>
// todo: add error checking if needed
class HardwareWrapper {
public:
HardwareWrapper(Hardware *pH)
: _pHardware(pH) {
InitializeAndCheckHardware(_pHardware);
}
~HardwareWrapper() {
DeInitializeHardware(_pHardware);
}
const Hardware *getHardware() const {
return _pHardware;
}
const Hardware *operator->() const {
return _pHardware;
}
const Hardware& operator*() const {
return *_pHardware;
}
private:
Hardware *_pHardware;
// if you don't want to allow copies...
HardwareWrapper(const HardwareWrapper &other);
HardwareWrapper& operator=(const HardwareWrapper &other);
}
// ...
void ControlLoop::main_loop()
{
HardwareWrapper hw(pHardware);
// code
}
Cette solution est très propre. Merci.
Étant donné qu'il existe une fonction initialisizxxx code>, je vous suggère de mettre l'initialisation dans le constructeur, de la symétrie.
goto n'est pas une bonne pratique pour sortir de la boucle lorsque Aussi, dans des routines complexes, il est bon de n'avoir qu'une seule logique de sortie (avec nettoyage) placée à la fin. Goto est parfois utilisé pour sauter à la logique de retour. P> exemple du pilote de bloc VMDK QEMU VMDK: P> pause code> est une option.
static int vmdk_open(BlockDriverState *bs, int flags)
{
int ret;
BDRVVmdkState *s = bs->opaque;
if (vmdk_open_sparse(bs, bs->file, flags) == 0) {
s->desc_offset = 0x200;
} else {
ret = vmdk_open_desc_file(bs, flags, 0);
if (ret) {
goto fail;
}
}
/* try to open parent images, if exist */
ret = vmdk_parent_open(bs);
if (ret) {
goto fail;
}
s->parent_cid = vmdk_read_cid(bs, 1);
qemu_co_mutex_init(&s->lock);
/* Disable migration when VMDK images are used */
error_set(&s->migration_blocker,
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
"vmdk", bs->device_name, "live migration");
migrate_add_blocker(s->migration_blocker);
return 0;
fail:
vmdk_free_extents(bs);
return ret;
}
Il y a plusieurs alternatives à Cependant, vous devez garder à l'esprit que les deux en général, si vous utilisez un Remarque: Si votre corps de fonction ne peut pas s'adapter confortablement sur votre écran, vous faites Cela ne va pas. em> p> p> goto code>:
pause code>,
Continuer code> et
retour code> en fonction de la situation.
pause code> et
continuer code> sont limités en ce sens qu'ils n'affectent que la boucle la plus interne.
retour code> d'autre part n'est pas affecté par cette limitation. p>
goto code> à sortie em> Une portée particulière, vous pouvez alors refacteur en utilisant une autre fonction et une instruction code> renvoyer code> à la place. Il est probable que cela facilitera le code de lire en tant que bonus: p>
Merci, c'est une autre solution valant la peine d'être envisagée.
Je vois des charges de personnes suggérant L'Inquisition contre Les points principaux du papier de Dijkstra sont les suivants: P>
pause code> au lieu de
goto code>. Mais
casse code> n'est pas "meilleur" (ou "pire") que
goto code>. P>
goto code> Commencé efficacement avec Dijkstra's "Aller à considéré comme nocif" de papier "de" dommages nuisible en 1968, lorsque le code spaghetti était la règle et les choses telles que la structure du bloc-structuré
si code> et
tandis que les déclarations code> étaient toujours considérées comme considérées Bord de coupe em>. Algol 60 les avait, mais c'était essentiellement une langue de recherche utilisée par les universitaires (cf. ml aujourd'hui); Fortran, une des langues dominantes à l'époque, ne les obtiendrait pas pendant 9 ans! P>
goto code> de toutes ses formes différentes, il est possible de connaître les choses em> sur les états possibles des variables à chaque position lexicale du programme. En particulier, à la fin d'un
pendant la boucle code>, vous savez que la condition de cette boucle doit être fausse. Ceci est utile pour le débogage. (Dijkstra ne dit pas tout à fait cela, mais vous pouvez en déduire.) Li>
ol>
pause code>, comme
goto code> (et précoce
retour code> s et exceptions ...), réduit (1) et élimine (2 ). Bien sûr, en utilisant
break code> vous permet souvent d'éviter d'écrire une logique compliquée pour le
pendant la condition code>, vous mettant un gain net dans la compréhensibilité - et exactement la même chose s'applique à
goto code>. p>
Merci pour l'explication. Il est également bon de comprendre que sans une pause code> ou goto code> dans mon cas particulier, le reste de la boucle tandis que la boucle doit être protégé par un
supplémentaire si Code>, car l'envoi de sortie sur le matériel basé sur une mauvaise entrée pourrait être désastreux, c'est-à-dire que la réponse correcte aux données incorrectes est "Arrêt maintenant".
"Cela n'a pas beaucoup de sens pour moi d'utiliser Raii" il est toujours logique d'utiliser Raii. Toujours. Tout le temps.
Pourquoi ne pas utiliser
pause code> ici?
S'il n'y a qu'une seule boucle que vous rompez, utilisez
break code>; Il est plus propre et plus clair. Si vous avez plusieurs niveaux de boucle, ou si vous êtes à l'intérieur d'un commutateur code> code>, et que vous devez quitter toutes les boucles, le
goto code> est ok.
Cela peut être une question plus appropriée pour programmeurs.stackexchange.com
Je pense qu'il a fait le point pourquoi il n'utilise pas de pause, et nous devrions discuter davantage au lieu de simplement demander pourquoi ne pas utiliser la pause
@Jamesmcnellis fait l'idée d'une sorte d'objet "session" sembler comme la voie à suivre ici?
Basé sur ce que vous avez montré ici, le conteneur Raii pourrait être aussi simple que
struct matérielsInitializer {explicite "HardwareInitializer (matériel * HW): _hw (hw) {initializeandcheckableware (_hw); } ~ HardwareInitializer () {DEINITIALIZEHARDWARDWARD (_HW); } Matériel * _hw; } code>. Il suffit de faire le nettoyage automatique.
Le matériel
supérieur ci-dessus code> exemple de classe doit être non gêné. Ces boîtes de commentaire sont trop petites pour le code d'écriture. Il est trop tard pour le changer, malheureusement. I>
@Jamesmcnellis c'est bon. Je comprends ce que vous voulez dire et que vous avez également mentionné dans la réponse d'ED S. ci-dessous. Est-ce juste pour empêcher l'erreur de l'utilisateur ou pourrait le laisser copier causer des bugs plus subtils?