Questions diverses sur C++

Le problème exposé dans ce sujet a été résolu.

Salut.

J’ai plusieurs questions sur le C++ et j’aurais voulu savoir si vous pouviez m’éclairer :).

Comment faire pour "bloquer" le programme et attendre que l’utilisateur veuille le fermer en appuyant sur une touche ? (J’utilise deux fois "std::cin.ignore();". Es-ce qu’il n’y a pas mieux ?)

Je n’ais pas trop compris la fonction de "std::cin.clear();", "std::cin.ignore(255, '\n’);" ainsi que "std::cin.fail();".

Edit : L’utilisation de auto pour les chaînes de caractère remplace juste std::string ou il y a d’autre conséquence ? Si j’utilise auto pour "auto variable{ 0 };" puis plus tard "variable = 0.1;" cela ne pose pas de problème ?

Enfin, es-ce que l’utilisation de "while(!std::cin » jour || jour < 1 || jour > 12);" est correcte ?

Merci d’avance ! ^^

#include <iostream>
#include <string> //permet l'utilisation des chaines de caractère.
#define NOMINMAX //permet l'utilisation d'accent 1/3.
#include <Windows.h> //permet l'utilisation d'accent 2/3.


int main()
{
    SetConsoleOutputCP(1252); //permet l'utilisation d'accent 3/3.
    using namespace std::literals; //permet l'utilisation de "auto" pour les chaines de caractère.


    std::cout << "Quel jour es-tu né ? : ";
    int jour{ 0 };
    std::cin >> jour;

    if (!std::cin >> jour || jour < 1 || jour > 31)
    {
        do
        {
            std::cin.clear();
            std::cin.ignore(255, '\n');
            std::cout << "Vous avez rentré une valeur non comprise entre 1 et 31.\nQuel jour es-tu né ? : ";
            std::cin >> jour;
        } while (!std::cin >> jour || jour < 1 || jour > 12);
    }

    std::cout << "Quel mois es-tu né ? : ";
    int mois{ 0 };
    std::cin >> mois;

    if (!std::cin >> mois || mois < 1 || mois > 12)
    {
        do
        {
            std::cin.clear();
            std::cin.ignore(255, '\n');
            std::cout << "Vous avez rentré une valeur non comprise entre 1 et 12.\nQuel mois es-tu né ? : ";
            std::cin >> mois;
        } while (!std::cin >> mois || mois < 1 || mois > 12);
    }

    std::cout << "Tu es né le " << jour << "/" << mois << "." << std::endl;

    std::cin.ignore();
    std::cin.ignore();

    return 0;
}
+0 -0

Comment faire pour "bloquer" le programme et attendre que l’utilisateur veuille le fermer en appuyant sur une touche ? (J’utilise deux fois "std::cin.ignore();". Es-ce qu’il n’y a pas mieux ?)

On peut faire mieux, mais il n’y a rien de standard. Le mieux étant de ne rien faire et de configurer l’IDE pour qu’il le fasse tout seul ou de lancer le programme dans une console (cmd).

Mais sinon std::cin.ignore() reste le plus simple. Si tu dois le faire 2 fois, c’est parce qu’il reste des données dans le flux (un saut de ligne généralement), le premier ignore() les surprime, le second attend que l’utilisateur écrive quelque chose.

Je n’ais pas trop compris la fonction de "std::cin.clear();", "std::cin.ignore(255, '\n’);" ainsi que "std::cin.fail();".

  • clear(): remet l’état du flux dans un état valide (s’il est invalide, toute opération dessus sera un échec)
  • fail(): indique si le flux est dans un état valide ou non
  • ignore(255,’\n’): vide le buffer du flux jusqu’à trouver le caractère \n dans une limite de 255 caractères. 255 est arbitraire, on préfère mettre la valeur maximale possible qui est std::numeric_limits<std::streamsize>::max().

Enfin, es-ce que l’utilisation de "while(!std::cin » jour || jour < 1 || jour > 12);" est correcte ?

Non. !std::cin >> jour est l’équivalent de (!std::cin) >> jour qui est un décalage binaire. Un compilateur bien configuré devrait émettre un avertissement. Le premier || n’est pas bon non plus, ceci devrait t’interesser: https://cpp.developpez.com/faq/cpp/?page=Manipulation-de-la-console#Comment-verifier-les-valeurs-saisies-avec-cin

(Et le reste de la page aussi. Ainsi que toute la FAQ.)

L’utilisation de auto pour les chaînes de caractère remplace juste std::string ou il y a d’autre conséquence ?

auto n’est pas un remplacement de std::string. C’est un mot clef qui déduit le type d’une variable en fonction du type de la valeur d’initialisation. Pour qu’une variable soit de type std::string, il faut que la valeur d’initialisation soit de type std::string.

using namespace std::literals; //permet l'utilisation du suffixe `s`

auto str = "une chaîne"s;
// str est de type std::string

Si j’utilise auto pour "auto variable{ 0 };" puis plus tard "variable = 0.1;" cela ne pose pas de problème ?

Si. Le type d’une variable ne change pas. Ici, variable est de type int car initialisé avec 0 qui est de type int. 0.1 est un double, elle valeur sera tronqué à 0 lorsqu’elle sera mise dans un type entier.


Concernant le code

  • Il n’y a pas de std::string, #include <string> n’est pas utile. "abc" sans le suffixe s n’est pas de type std::string, mais un vilain tableau C.
  • #define NOMINMAX se serait mieux de le faire dans les options de compilation car cela n’a de sens que pour Windows et - de mon point de vu- cette macro devrait toujours exister.
  • #include <Windows.h>, SetConsoleOutputCP(1252): pas standard est valide uniquement sur Windows. Je préfère http://h-deb.clg.qc.ca/Sujets/AuSecours/Afficher—Accents.html qui a le mérite de n’utiliser que des éléments du standard. On peut aussi mettre le code dans des macros conditionnelles pour que le code n’existe pas sous les autres plate-formes. Ou configurer le terminal de sortie pour qu’il supporte les accents.
  • int jour{ 0 }; std::cin >> jour;: C’est à contre courant de ce qui se dit généralement, mais je préfère ne pas initialiser une variable lorsque celle-ci le sera juste après. L’objectif est que si std::cin ne peut pas initialiser la valeur suite à une erreur, mais que je la lise quand même (problème dans le code), il y a des erreurs à l’exécution. Les outils d’analyse détecte très bien l’utilisation de variable non initialisé, mais ne feront rien si la valeur est initialisée avec une valeur fictive. Mais dans le cas général, il faut initialiser les variables.

Ok donc pour les variable autant mettre le type directement pour éviter les erreurs ?

Et je n’ai pas saisi ceci : "Il n’y a pas de std::string, #include <string> n’est pas utile. "abc" sans le suffixe s n’est pas de type std::string, mais un vilain tableau C." Je comprend pour le coup que #include <string> ne sert à rien (je laisse tout ça pour ne pas avoir à le remettre en vérité ^^) mais le coup du vilain tableau C. Tu insinues que c’est pas bon ?

(!(std::cin » jour) || jour < 1 || jour > 12) est correcte ?

Merci à toi en tout cas !

+0 -0

Ok donc pour les variable autant mettre le type directement pour éviter les erreurs ?

Pas forcement. Lorsqu’on utilise un itérateur par exemple, on se fiche d’indiquer le type, car c’est verbeux et cela n’apporte pas plus d’information. Dans une expression du style std::unique_ptr<T> p = std::make_unique<T>(), cela est redondant. Dans les cas où on se focalise plus sur le type que la valeur, oui c’est utile.

Et je n’ai pas saisi ceci : "Il n’y a pas de std::string, #include <string> n’est pas utile. "abc" sans le suffixe s n’est pas de type std::string, mais un vilain tableau C." Je comprend pour le coup que #include <string> ne sert à rien (je laisse tout ça pour ne pas avoir à le remettre en vérité ^^) mais le coup du vilain tableau C. Tu insinues que c’est pas bon ?

Non, juste que le C++ est piégeux. Un littéral de chaîne (cf: "...") est un tableau C, pas un std::string (et les tableaux C, c’est vilain). C’est pour cela que le suffixe s à été introduit. J’avais l’impression que tu mettais l’include pour utiliser "...".

(!(std::cin » jour) || jour < 1 || jour > 12) est correcte ?

Plus ou moins. Dans la plupart des cas oui, mais cela ne prend pas en compte l’état eof (end of file) du flux ce qui peut engendrer une boucle infinie. Regarde comment c’est fait dans la FAQ de dvp (lien dans le poste précédent).

Dans le cas ou c’est une saisie clavier que l’on attend, quel est son intérêt ?

Le flux peut être coupé manuellement (ctrl+D (linux), ctrl+Z (windows)) ou par quelque chose d’extérieur. Dans les 2 cas, std::cin passe en eof.

Perso, je le traite comme un fin de programme/sortie de menu. Dans le cas d’un jeu se serait ctrl+D -> fin de partie, retour au menu -> ctrl+D -> quitter. Et si je fais un programme qui simule les touches et qu’il s’arrête subitement, je voudrais bien que le jeu s’arrête aussi. Si le eof n’est pas correctement géré, le programme va tourner en boucle indéfiniment.


Pour faire une rapide simulation de touche sur une console: programme_du_jeu < nom_du_fichier_contenant_le_texte_à_simuler

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte