Je viens de me lancer dans ce cours et j'aimerais connaître l'avis de la communauté sur ce dernier, étant donné que mes recherches sur le forum m'ont ramené à des postes de 2017 disant qu'il n'était pas bon.
J'ai presque terminé la première partie et en faisant quelques recherches j'ai constaté qu'il y a avait des choses qui ne m'étaient pas apprises correctement et que j'ai dû rectifier en cours de route, comme le faite de ne plus déclarer l'utilisation du namespace std en début de code et de l'écrire manuellement à chaque fois qu'on utilise un "cin", "cout", etc... afin d'éviter les problèmes par la suite.
Si tu as déjà trouvé des discussions datant de 2017 disant qu'il n'était pas bon, tu peux te dire que rien n'a changé depuis l'époque.
Ce cours n'as vraiment rien d'un grand vin: il ne s'améliore pas avec l'age! bien au contraire
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Tous n'est pas à jeter , certaines choses sont vraies mais malheureusement beaucoup de choses sont fausses
Il n'y a qu'une seule chose qui soit réellement correcte, c'est la syntaxe.
Encore une chance, sinon tes programmes ne compileraient tout simplement pas
Mais, pour le reste ...
les images aidant au choix de Code::Blocks n'ont jamais que 5 versions de retard
Dans les images, justement, lors de la création du projet, il conseille de décocher la case "create Debug configuration", alors que c'est justement sur cette configuration que tu vas passer le plus de temps (la configuration "release" ne sera utilisée qu'une fois que tout fonctionne)
Il te parle de Visual Studio 2010. Il n'y a jamais eu que cinq versions depuis, mais c'est pas grave, n'est-ce pas ?
Et ce n'était que mes constatation pour la première page. Passons à la deuxième:
J'ai bien conscience que le code qu'il nous montre est le code automatiquement généré par Code::Blocks, Mais regardons le d'un peu plus près! Il prend la forme de
#include <iostream>
using namespace std;
int main()
{
cout << "Hello world!" << endl;
return 0;
}
Heu...
Savais tu que la directive using namespace std; n'a été fournie par le comité que parce qu'il a décidé, en 1998, de faire passer l'ensemble de la bibliothèque standard dans l'espace de nom std, mais que, du coup, toute la base de code qui avait déjà été écrite risquait de ne plus compiler, pour la simple et bonne raison que la bibliothèque standard se trouvait avant dans l'espace de noms global?
Du coup, cette directive a été créée pour permettre aux projets "anciens" de continuer à compiler "avec un minimum de modifications": on plaçait cette directive dans un fichier d'en-tête inclus partout ailleurs (par exemple, dans le fichier config.h, qui est souvent présent), et "roulez jeunesse".
Oui, mais, on peut difficilement prétendre que le code que l'on écrit aujourd'hui (en 2020!!!) date ... d'avant 1998, nous sommes d'accords? Et puis, cette directive pose bien plus de problèmes qu'elle n'en résout (je t'en parlerai si tu y tiens).
Ensuite, il parle de la directive #include. C'est sympa... J'aurais, personnellement, beaucoup aimé qu'il explique un peu mieux ce qu'elle fait effectivement, car elle laisse pas mal de questionnements aux lecteurs
Pour le reste, disons que nous pourrions regretter qu'il n'explique pas que std::endl (car il devrait toujours utiliser le nom pleinement qualifier pour représenter les éléments de la bibliothèque standard, afin d'éviter au lecteur de croire que "ca vient d'ailleurs) ne se contente pas de provoquer un retour à la ligne, mais qu'il provoque également un "flush" (la vidange complète) du tampon de la sortie standard.
Nous pourrions aussi regretter qu'il ne précise pas ce std::cout représente en réalité la sortie standard qui correspond, sur nos configurations classiques, le plus souvent la console, mais qui pourrait "tout aussi bien" correspondre à "autre chose" (comme je sais pas, moi, au hasard: une tablette braille).
Passons à la troisième page:
Lorsqu'il nous parle des noms (de variables), il nous dit
si le nom se décompose en plusieurs mots, ceux-ci sont collés les uns aux autres
Il pourrait, au moins, préciser que l'on peut aussi utiliser l'underscore _, et qu'un nom proche de la_poupee_qui_tousse sera aussi correct que laPoupeeQuiTousse (en fonction de la convention de nommage utilisée).
Un peu plus loin, il nous donne une liste des types de données que l'on peut utiliser. Elle est "suffisante" dans un premier temps, mais clairement incomplète si on ne prend que les types primitifs en compte (il manque le type long, le type long long, les versions unsigned de tous les types entiers, le type float et le type long double pour qu'elle soit complète).
Pire encore, il place le type std::string dans la même liste, sans utiliser le nom pleinement qualifié, ce qui laisse croire à l'utilisateur que c'est le même genre de type de donnée que tous les autres de la liste. Ce qui est tout à fait faux! :
Les types comme bool, char, int (et les versions unsigned équivalentes) ou double sont des types primitifs, que le compilateur C++ connaît d'office alors que std::string vient de la bibliothèque standard, et qu'il faut donc inclure un fichier d'en-tête spécifique pour que le compilateur en ait connaissance.
Vient ensuite l'initialisation des variable, et son fameux
TYPE NOM (VALEUR);
ou
TYPE NOM = VALEUR;
Le premier a été complètement abandonné depuis 2011 (cela fait quand même 9 ans!!!) et, si le deuxième est encore utilisé, nous préférerons désormais quand même toujours utiliser la syntaxe
TYPE NOM {VALEUR};
Quant à son "astuce pour gagner de la place", qui consiste à regrouper la déclaration de plusieurs variables de même type sur une même ligne en les séparant par une virgule...
Bons dieux, mais quand est-ce que l'on arrêtera avec ces conneries????!!!!
D'abord et avant tout, cela rend le code particulièrement difficile à lire, dans le sens où il est "beaucoup trop facile" de ne pas se rendre compte qu'une variable est déjà déclarée
Ensuite, et surtout, la règle devrait toujours être de déclarer les variable au plus près de leur première utilisation, et, de préférence, lorsqu'il est possible de leur donner une valeur directement utilisable par l'utilisateur.
Au moins, on peut être content pour une chose: il parle très rapidement des références. C'est peut-être le seul point positif de cette page Passons donc à la suivante.
Sur la quatrième page, il nous parle de récupérer les informations introduites par l'utilisateur.
Il nous parle de std::cin et de std::getline, ce qui est déjà pas mal, mais il oublie de préciser LA chose, parmi les choses simples qui a sans doute provoqué le plus de problèmes, de crashes et de résultats aberrants dans les programmes : l'utilisateur est un imbécile distrait et on ne peut pas accorder la moindre confiance aux informations qu'il nous transmet.
Autrement dit, chaque fois que vous demandez à l'utilisateur d'introduire une donnée quelconque, vous devez vous assurer que ce qu'il aura introduit respecte les règles spécifiques devant être appliquées à cette donnée. Mais ca, il n'en parle nulle part. Un peu comme s'il vivait dans le monde des bisounours, où tout le monde il est beau, tout le monde il est gentil.
Un peu plus bas, il va nous parler des constantes. Mais il a déjà un train de retard, car il devrait en profiter pour parler des constantes de compilation (et en profiter pour expliquer la différence) constexpr, qui sont apparues depuis 9 ans, quand même.
Il faut attendre la cinquième page (de la version HTML) pour en trouver une sur laquelle il n'y a aucune remarque à faire (enfin, après une relecture rapide ).
Sur la sixième page, celle qui parle des fonctions, il nous présente la liste des argument comme étant
les données avec lesquelles la fonction va travailler
C'est vrai... Mais ce n'est pas complèt
Primo, parce que cela pourrait sous entendre qu'une fonction ne pourrait pas avoir de variables locales (ce qui est complètement faux) et
secundo parce que les arguments correspondent, surtout, aux données dont la fonction est incapable d'évaluer la valeur par elle-même
Ensuite, sa fonction toute simple qui prend la forme de
int ajouteDeux(int nombreRecu)
{
int valeur(nombreRecu + 2);
return valeur;
}
(j'ai supprimé les commentaires par facilité) est correcte, on peut difficilement dire le contraire...
Cependant, il laisse planer l'impression que les paramètres reçus par les fonctions seraient en quelques sortes des "données de second rang" qui ne peuvent pas être utilisées en tant que variables (quand ils ne sont pas constants) parce que nous serions obligés de déclarer une nouvelle donnée (valeur) pour représenter le résultat.
Or, un code proche de
int ajouterDeux(int valeur){
valeur+=2;
return valeur;
}
serait tout à fait correct! Et je ne parle même pas de présenter le sucre syntaxique (si cher à nos coeurs) qui nous permettrait d'écrire le code
int ajouterDeux(int valeur){
return valeur+2;
}
De même, il présente le passage par référence constante (lorsque le paramètre est une std::string) comme
une application bien pratique (...) Si votre chaîne de caractères contient un très long texte (la totalité de ce livre par exemple !)
Ce qui laisse entendre que, ben ... pfff ce n'est "pas si grave" de passer des std::string par valeur, hein?
FAUX!!: La copie occasionnée par le passage par valeur de n'importe quelle donnée "prenant d'avantage de mémoire qu'un type primitif" devrait toujours être évitée grâce au passage par référence (éventuellement constante). Quel que soit le type de la donnée, quelle que soit la valeur de la donnée transmise.
Mais bon, le reste de la page n'est "pas trop mauvais", du moins, pas au point de me faire dresser les cheveux sur la tête.
La septième page est dédiée à l'utilisation des tableaux... OUCHHH....
Il commence par nous présenter les tableaux de taille statique (définie à la compilation). Au moins, il n'oublie pas de préciser que leur taille doit toujours être une valeur constante, même s'il aurait pu le mettre plus en évidence.
Vient le moment où il nous parle de transmettre un tableau en paramètre à une fonction. Il nous dit explicitement que
Il ne faut rien mettre entre les crochets.
(...)Oui, je sais c'est ennuyeux. Mais il ne faut pas vous en prendre à moi, je n'ai pas créé le langage.
FAUX!!! Si on donne la taille d'un tableau (statique, dans le cadre qui nous intéresse), il faut juste veiller à ce que la taille soit une valeur constante.
En plus, son exemple de fonction moyenne manque cruellement de const-correctness...
Il nous le dit lui-même:
La deuxième restriction est qu'un tableau statique est toujours passé par référence
Ce qu'il oublie de dire, c'est que si on veut garantir que la fonction n'essayera pas d'aller modifier le contenu du tableau, il faut le dire explicitement au compilateur en transmettant le tableau sous une forme constante
compilera parfaitement et fournira le résultat qu'on lui demande, à savoir (par exemple)
./a.out
# on introduit les notes
Veuillez introduire la note 1 :19.5
Veuillez introduire la note 2 :14
Veuillez introduire la note 3 :6.5
Veuillez introduire la note 4 :12
Veuillez introduire la note 5 :11
# on affiche les notes
note 1 =19.5
note 2 =14
note 3 =6.5
note 4 =12
note 5 =11
# on affiche la moyenne des notes
la moyenne est de 12.6
On regrettera cependant qu'il n'ait pas pris la peine de parler de std::array que l'on préférera utiliser depuis C++11
Vient ensuite le tour de std::vector, dont on regrettera une fois de plus qu'il n'ait jamais mentionné le nom pleinement qualifié (mais bon, à ce stade, ce n'est qu'un détail).
La page suivante parle de l'utilisation des fichiers.
On regrettera encore une fois qu'il n'utilise jamais les noms pleinement qualifiés std::ifstream et std::ofstream, mais bon, cela devient une habitude, n'est-ce pas?
On regrettera sans doute plus encore qu'il continue à utiliser la syntaxe pré 2011 lorsqu'il s'agit d'ouvrir un fichier dont le nom est donné par une std::string, à savoir
ostram monflux(nomFicher.c_str());
Parce que, avant 2011, il fallait effectivement transmettre le nom du fichier sous forme d'un char *. Mais les choses ont changé depuis maintenant 9 ans!!! On peut ouvrir un fichier en lui transmettant le nom du fichier à ouvrir sous la forme d'une std::string, à savoir
Ah, au fait, tu as remarqué? je n'ai, jusqu'à présent, pas encore une fois cité la classe std::exception! Mathieux non plus d'ailleurs :P
Or, arrivé à la huitième page du cours, les raisons pous lesquelles une exception pourrait te claquer à la figure sont déjà très nombreuses. Ce serait pas mal d'être en mesure de les gérer dés maintenant, tu crois pas?
On remarquera aussi qu'il n'a parlé ni des range based loops (les boucle prenant la forme de for(element : tableau) , ni de l'inférence de type (le mot clé auto) qui facilitent pourtant tellement la vie du développeur
En outre, il nous parle de tellg()/ tellp()/ seekg() /seekp(), un peu comme s'il était tout à fait normal de commencer à se balader dans notre fichier.
Il oublie juste de préciser que l'accès à un fichier -- que ce soit en lecture ou en écriture -- est typiquement le genre d'accès le plus lent (à l'exception du réseau) auquel nous pouvons être confronté sur un ordinateur.
Résultat des courses, il ne se passe pas deux mois sans qu'il n'y ait un débutant qui vienne poser une question sur le forum parce que "mon jeu est décidément très lent", alors qu'il passe son temps à se balader dans ses fichiers.
La neuvième page est le parfait exemple de tout ce que je viens de dire:
Passons sur l'usage de srand() et de rand(), qui sont des fonctionnalités issues du C et qui posent de sérieux problèmes, alors que nous préférerons les possibilités offertes (depuis 2011) par le fichier d'en-tête <random>, même si c'est aussi le genre de choses que l'on passe notre temps à répéter sur le forum.
Il y a tellement de discussions sur le forum concernant le TP du mot mystère que je ne vais pas revenir sur cette page
La dixième page a trait à une notion très importante (surtout en C): les pointeurs. Sauf que, en C++ moderne, elle ne devrait pas se trouver ici...
Ben oui, la notion de pointeurs ne devient très importante en C++ qu'à partir du moment où l'on commence à jouer avec des notions comme l'héritage ou le polymorphisme. Or, à ce niveau-ci, on est encore très loin de le faire
En plus, il nous fait jouer avec des new et des delete, mais il ne parle ni des pointeurs intelligent (dont std::unique_ptr en priorité), ni de la notion de propriété de la ressource pour laquelle on a eu recours à l'allocation dynamique
Par la suite, on arrive dans la partie OO... Et on en a déjà tellement parlé que j'hésite à revenir dessus
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Je recrée un compte pour réagir au post très utile de @koala01. (Mon compte initialement créé en 2007 et supprimé après la sortie de OC.)
Autant dire que ça fait un petit moment que j'ai pas remis les pieds sur les cours du SdZ et même si j'ai appris sur ce site, mon niveau a quelque peu évolué depuis. Je n'aurais peut être pas dû le relire car dans mes souvenirs ce n'était pas aussi mauvais.
Il y a juste un point sur lequel j'aimerais intervenir sur lequel, je trouve, @koala01 a été beaucoup trop gentil. C'est sur les tableaux. C'est à se demander si l'auteur sait de quoi il parle ; pourtant, Mathieu Nebra n'est pas un incompétent, mais quand je relis ce chapitre les cheveux se dressent sur ma tête. Je peux comprendre qu'il faille simplifier, vulgariser pour s'adapter au niveau du lecteur ; sauf que ce n'est pas une excuse pour raconter des choses qui sont factuellement incorrects et pour volontairement entretenir la confusion sur des notions essentielles du langage.
Non.Non, en C++ (comme en C) on ne peut pas passer un tableau en paramètre à une fonction ! Ce n'est tout simplement pas possible ! Il existe, malheureusement, une syntaxe void f(int arr[]) qui est un triste et dangereux héritage du C. Avec cette syntaxe, derrière ce paramètre qui se fait passer pour un tableau, se cache en réalité un pointeur déguisé. Donc quand on écrit :
Dans f(), arr est un pointeur, et non un tableau malgré les apparences ; donc déférencer et assigner une valeur à cette lvalue modifie le tableau dans le main. C'est le « un tableau statique est toujours passé par référence » ; mais non ! Il n'y a pas de référence ! Il n'y a même pas d'esperluette ! C'est quand même ouffissime que l'auteur mette en garde sur ce point mais en donnant une explication complètement à coté de la plaque. Je pense que l'auteur ignore complètement qu'il s'agit d'un pointeur en fait, et invente un concept (le passage de tableau par référence) pour justifier un comportement qu'il ne comprend pas.
Et malheureusement ça a de tristes conséquences, car cette syntaxe entretient la confusion entre tableau et pointeur (et vous remarquerez qu'absolument nul part il n'est dédié de paragraphe à mettre au clair ce point très important, c'est de l'inconscience à ce stade) dans un langage où les tableaux se castent implicitement en pointeur et où l'opérateur [] s'applique aux deux, donnant l'impression que tableaux et pointeurs sont interchangeables.
Mais ce n'est pas tout ! Sinon ça ne serait pas drôle… Autre conséquence, plus inquiétante cette fois, tient à la sécurité. Comme le paramètre de type T a[N] est un réalité un pointeur, sa taille (sizeof) est celle d'un pointeur et non celle du tableau qu'il prétend être ; et ce, combien même on précise une taille ! Évidemment, à ce stade du cours, on ne peut pas attendre du lecteur qu'il s'alarme sur le caractère optionnel de cette taille qui devrait pourtant mettre la puce à l'oreille que quelque chose ne va pas du tout. Est-ce juste une erreur de débutant ? Même pas ! Même les développeurs du kernel se prennent les pieds dedans, source : https://lkml.org/lkml/2015/9/3/428
Je trouve aussi très questionnable le terme de « tableau statique ». Les tableaux ont toujours une taille fixe (si on exclut les VLA), et non, un std::vector n'est pas un tableau dynamique. Ce n'est pas parce qu'on peut utiliser l'opérateur [] que boum c'est un tableau (cf. confusion tableau / pointeur).
Mais bon, je suppose qu'il est plus facile de dire que (je cite, c'est pas une blague) « les tableaux et les fonctions ne sont pas les meilleurs amis du monde ».
Alors que faire ? Qu'est-ce que je recommande comme ressource pour les débutants ? Honnêtement je ne sais pas, je me demande même s'il y a vraiment une bonne façon de débuter dans ce langage. Mais nier sa complexité (qui est stratosphérique et ça s'améliore pas) n'est probablement pas la bonne approche ; apprendre le C++ c'est forcément en chier pendant des années et de pondre des bouses pas possibles. Mon avis, controversé, est de débuter par le C. Ça donne des balises solides et réalistes en développement natif sur lesquels construire des notions plus avancées. Je ne crois pas aux arguments des « mauvaises habitudes du C », si une personne ne sait pas faire la part des choses, qu'elle rende tout de suite son tablier. Dans mon travail, je développe à la fois en C et en C++ (selon les projets que je maintient), il n'y a aucun mélange des genres.
Je tiens à saluer à nouveau @koala01 pour son très bon post, malheureusement une voix qui m'a l'air minoritaire ici.
Évidemment, ce n'est pas ça qui est retrouvé dans le cours. D'un point de vu pédagogique le premier est un chouilla complexe côté syntaxe. Et le second, il faut attendre le C++11 pour une existence officielle -- mais cela reste exprimable en C++98, cf boost::array<> Parenthèse avec le C++20, on a les span, mais on ne peut pas attendre une telle parenthèse d'une ressource de 2010/2011
> Je trouve aussi très questionnable le terme de « tableau statique »
C'est la traduction directe du terme officiel "static array". On peut difficilement critiquer ici.
> un std::vector n'est pas un tableau dynamique
Stricto-sensu c'est vrai. Mais cela reste une première approximation plus que suffisante pour disposer de tableaux dont la taille n'est connue qu'à la compilation. Mieux, c'est celle que plus d'un parmi nous estime être la première forme de tableau de taille non connue à la compilation qu'un débutant en C++ devrait connaître. Et celle qui devrait être utilisée par défaut en industrie, jusqu'à preuve que des gains de performances significatifs peuvent être obtenus avec des alternatives.
> Qu'est-ce que je recommande comme ressource pour les débutants ?
Nous sommes plusieurs à recommander le tuto en cours de rédaction sur Zeste de Savoir, ou le C++ Primer de Jossutis & cie en livre.
> Mon avis, controversé, est de débuter par le C. Ça donne des balises solides et réalistes en développement natif sur lesquels construire des notions plus avancées. Je ne crois pas aux arguments des « mauvaises habitudes du C », si une personne ne sait pas faire la part des choses, qu'elle rende tout de suite son tablier.
Là nous ne serons pas du tout d'accord. Nous sommes quelques uns à privilégier une progression pédagogique plus tranquille qui n'exige pas de comprendre ce qu'est un pointeur dès la seconde leçon.
Les habitudes critiquées, sont
1- Celles de croire que la gestion des erreurs et des ressources est semblable entre les deux langage -- les exceptions changent complètement la donne. Et le C++ offre une facilité dont il faut abuser: le RAII.
2- Sur le plan pédagogique, trop de cours de C donnent des mauvaises pratiques parce qu'un if toutes les 2 lignes (cf heuristique de reconnaissance rapide de code mauvais C de Raymond Chen), ce n'est pas possible avec un débutant. Du coup, les mauvaises habitudes prennent dès le début: car "pourquoi s'embêter avec les situations non nominales?". L'avantage du C++, c'est que l'on peut montrer du code correct et de qualité à des débutants sans sacrifier la pédagogie. Style de code qui est parfaitement exploitable en industrie.
> Je tiens à saluer à nouveau @koala01 pour son très bon post, malheureusement une voix qui m'a l'air minoritaire ici.
Je peux tout de suite te rassurer. Sa voix n'est pas minoritaire. Il est juste moins usé que les autres par les trolls récurrents. Et sa plume est juste infatigable.
> Non. Non, en C++ (comme en C) on ne peut pas passer un tableau en paramètre à une fonction
Si si, en C++ on peut avec une référence: void f(int (&arr)[n]). C'est d'ailleurs ainsi que fonctionnent std::begin/end/size.
> et non, un std::vector n'est pas un tableau dynamique.
Et du coup c'est quoi ? Je cite la doc quand même: std::vector is a sequence container that encapsulates dynamic size arrays.
> je me demande même s'il y a vraiment une bonne façon de débuter dans ce langage. Mais nier sa complexité (qui est stratosphérique et ça s'améliore pas) n'est probablement pas la bonne approche ; apprendre le C++ c'est forcément en chier pendant des années et de pondre des bouses pas possibles.
On peut très facilement ignorer la complexité sans pour autant apprendre de mauvaise chose. Il suffit de commencer par les bonnes choses. Exemple, ne pas voir les tableaux C alors que std::array n'a pas de comportement exceptionnel. C'est même plutôt conseillé dans un cours pour débutant, inutile de lui montrer les cas pathologiques, alors qu'il n'a pas encore bien digéré ceux normaux.
Certes, le langage se complexifie, mais il devient aussi moins complexe. De nouvelle classes et syntaxe existent pour simplifier son utilisation de tous les jours comme de nouvelles classes et syntaxe apparaissent pour les plus experts. Il faut apprendre de manière graduelle, en utilisant les simplifications fournit pour ensuite apprendre comment faire ces simplifications. C'est beaucoup plus simple d'utiliser std::vector que de se taper l'allocation dynamique à la main. C'est aussi beaucoup plus simple de comprendre comment utiliser une boucle sur intervalle que d'ajouter soi-même sont support dans une classe.
> Mon avis, controversé, est de débuter par le C. Ça donne des balises solides et réalistes en développement natif sur lesquels construire des notions plus avancées.
Bof, le C est très simple, mais en contre-partie est très difficile à maîtriser. La plupart des débutants se cassent les dents sur les pointeurs. En C++ on peut les éviter longtemps, en C, on est vite limité. Il n'offre pas non plus un aussi haut-niveau d'abstraction que C++, ce qui je trouve le rend assez désagréable. Du coup, on se prend à la fois l'apprentissage du langage et ce qui pourrait être des détails d'implémentations.
> Je ne crois pas aux arguments des « mauvaises habitudes du C », si une personne ne sait pas faire la part des choses, qu'elle rende tout de suite son tablier. Dans mon travail, je développe à la fois en C et en C++ (selon les projets que je maintiens), il n'y a aucun mélange des genres.
Sur le forum, on remarque très vite lorsqu'une personne est avant tout développeur C ou même Java/C#. Il y a des automatismes qui ne sont pas souhaitables en C++. Faire la part des choses n'est pas aisé, d'autant plus lorsqu'on débute et qu'on s'accroche à ce que l'on connaît déjà. Comme le C et le C++ on nue base syntaxique assez proche, s'accrocher est très facile.
Après tant d'années à faire régulièrement référence à ma boule de crystal, j'ai enfin l'occasion de vous parler d'un autre outil dont j'use et dont j'abuse:
La plume à papote ... Les lecteurs de Harry Potter comprendront
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
> En fait si, on peut forcer la restriction à passer des tableaux en C++
int (&ref)[42] est une référence sur un tableau, pas un tableau, la référence faisant partie du type. Quant à std::array ce n'est pas un tableau non plus mais un template de structure qui contient un tableau ; qui plus est ici, une référence sur ce type.
> C'est la traduction directe du terme officiel "static array". On peut difficilement critiquer ici.
Peux-tu me donner tes références ? Car je viens de rechercher dans le dernier brouillon N4659 de C++17 et il est fait mention à chaque fois de « array » et je n'ai trouvé aucune occurrence de « static array ». Même chose sur cppreference.com. Mais ce n'est pas le fond de mon propos, je voulais simplement souligner qu'il n'y a pas d'« array dynamique » en C++ (à part les VLA encore une fois). Il faut allouer dynamiquement la mémoire et on se retrouve obligatoirement avec un pointeur. Que cette gestion se fasse dans un conteneur utilitaire (fournissant très justement un mécanisme RAII) comme std::vector n'y change rien.
> Les habitudes critiquées, sont [snip]
Tu prêches un converti ! Je m'excuse, je n'ai pas été assez clair. Je ne dis nullement que la façon de développer en C peut être transposée en C++. Bien sûr que le changement de paradigme est très fort entre les deux langages. Je disais simplement que les connaissances en C fournissent des bases solides pour le C++ : maîtriser les pointeurs, les accès mémoires, être sensibilité aux comportements indéfinis, aux durées de vie, à la gestion des ressources (mémoire, fd, etc.). Mais aussi à des concepts plus généraux : culture système (processus, système de fichier, appels systèmes, signaux, IPC, etc.), endianness, alignements mémoire, comptage de référence ; et j'en passe beaucoup, je prends ce qui me passe par la tête.
Je crois qu'il faut connaître (pas nécessairement maîtriser) ces notions de programmation native avant d'attaquer le C++ car sinon le mur est juste beaucoup trop haut. Le C++ est un langage qui est insupportablement complexe, un langage qui construit des abstractions au dessus de ces notions déjà abordées plus simplement en C. Je vois le C comme une première marche, une bouchée plus facile à avaler. Je pense que notre différent repose avant tout sur le fait que je ne crois pas que le C++ soit un bon langage pour débuter la programmation, et que c'est une technologie qui s'adresse à des développeurs expérimentés. Mais ça ne m'empêche pas de trouver l'argument contrairement parfaitement logique et raisonnable.
> int (&ref)[42] est une référence sur un tableau, pas un tableau, la référence faisant partie du type. Quant à std::array ce n'est pas un tableau non plus mais un template de structure qui contient un tableau ; qui plus est ici, une référence sur ce type.
Certes, mais ce n'est pas important car on ne notera pas de différence. Les pattes sont palmées, il y a un bec, c'est un canard et ça nous suffit. Et contrairement au "decay to pointer" de ta critique initiale, le bec n'est pas accroché avec un élastique.
Bref, fonctionnement, c'est un tableau dans les deux cas et sans les problèmes hérités du C.
> static array
Diantre. Ce n'est effectivement pas un terme officiel du C++. Comme les méthodes, et les variables c'est un machin externe qui n'apparrait pas dans les normes du C et du C++. Je ne sais pas d'où c'est hérité. Du coup, plus aucune pitié pour moi: au sens large le vecteur modélise bien un tableau dynamique.
Sur cppreference et sur C++CoreGuideline, google me trouve une occurrence de "static array" sur chacun. Dans un commentaire pour l'un et dans une explication pour l'autre.
Après le fait qu'il n'y ait pas dans le langage de type tableau dynamique en natif, mais que l'on retrouve une abstraction qui en a les propriétés me suffit amplement. Au fond, ces termes impropres, ce qui compte, c'est la notion, le "Modèle"/"Concept" qui est derrière. A ce niveau là, ça me suffit. Maintenant, je m'entends déjà pinailler dans le futur en disant que ces termes sont impropres, mais que les artefacts qui les modélisent en C++ sont "bla bla liste"
Merci donc pour cette digression.
> Je disais simplement que les connaissances en C fournissent des bases solides pour le C++ [...]
Je ne range pas vraiment ces choses dans les bases solides, mais dans les annexes à connaître un jour. Sur un plan totalement pédagogique, ce n'est pas important, pire, pour beaucoup cela sera nuisible. Il y aura toujours quelques exceptions qui voudront avoir une idée de la machine abstraite modélisée par le C et le C++ pour "comprendre" ce qui se passe.
Je prends volontiers l'exemple de l'Ada. Dans un cursus pédagogique, on peut manipuler tableaux, chaines, types abstraits de données sans avoir besoin de savoir comment écrire un vecteur non redimensionnable comme le mort-né std::dynarray. Le C++, après avoir accepté la syntaxe bizarre à base de chevrons permet la même chose.
Les pointeurs, honnêtement, on n'en a vraiment besoin que l'on l'on commence à manipuler des entités ou à vouloir écrire certains TAD comme des listes chaînées ou des arbres. Le débutant peut faire sans un bon moment (cf la vielle expérience de Francis Glassborrow, traduit en "Je me Lance"). Comme il peut faire sans savoir quelle est la modélisation générique type des piles pour passer paramètres & cie -- et là viennent les registres, les compilos qui inlinent...
Tout ça sont pour moi des détails qui deviennent intéressant si on veut maitriser le langage, mais qui en utilisation quotidienne ne sont pas si critiques. Savoir concevoir correctement en découpant bien nos fonctionnalités (coucou SRP & cie), une hiérarchie (coucou OCP et LSP) sont bien plus critiques. Faut-il pour autant voir un bon langage OO avant? Et quid de LISP et Prolog pour savoir réussir nos métaprogrammes?
J'espère ne pas m'attirer les foudres de tout le monde après avoir posté sur un autre sujet.
La seule vraie qualité du cours d'OC qui est le défaut de ZDS est qu'il est plus pédagogique.
Mais ça s'arrête là ...
Merci à tout le monde pour vos contributions à ce post.
Je vais prendre le temps pour le relire.
Oui c'est d'ailleurs pour ça que beaucoup de débutants sont si déçus que il soit obsolète, au moins si tu es (très) attentif tu peux bien t'en sortir alors que peu de sites sont aussi pédagogiques , de même pour es livres qui nécessite quasiment tous une connaissance d'un autre langage pour s'en sortir
J'espère ne pas m'attirer les foudres de tout le monde après avoir posté sur un autre sujet.
La seule vraie qualité du cours d'OC qui est le défaut de ZDS est qu'il est plus pédagogique.
Mais ça s'arrête là ...
Merci à tout le monde pour vos contributions à ce post.
Je vais prendre le temps pour le relire.
A vrai dire, à ce niveau là, c'est bien plus un inconvénient qu'un avantage Mais laisse moi t'expliquer pourquoi:
Le cours, il faut le reconnaître, est très (trop) bien écrit. A tel point que le lecteur -- dont il faut partir du principe qu'il ne connaît rien à la programmation, a l'impression de comprendre sans problème tout ce qu'il lit.
Car, il faut l'avouer, les phrases sont courtes, il n'y a pas de tournures "alambiquées" et il n'y a pas de mot vraiment compliqué à comprendre, à part un ou deux termes techniques (et encore) qui sont eux-même **relativement bien" expliqués**
Si bien que les débutants sont persuadés d'avoir parfaitement compris -- et à juste titre d'ailleurs -- ce qu'ils ont lu, et, du coup, ils coient, pour faire simple, comprendre parfaitement le langage.
Sauf que ce qu'ils ont lu couvre quoi? dix pourcents (et pas les meilleurs) des capacité du langage?
Sauf que ce qu'ils ont lu correspond à des techniques et à des habitudes que l'on combat depuis vingt ans!
Sauf que ce qu'ils lisent était déjà jugé comme "une manière incorrecte d'envisager le langage" au moment où il a été rédigé.
Herox Codeur a écrit:
Oui c'est d'ailleurs pour ça que beaucoup de débutants sont si déçus que il soit obsolète
Ce n'est pas tant le fait qu'il soit obsolète qui pose problème. Si le seul reproche qu'on avait pu lui faire avait été -- pour faire simpe -- de n'avoir pas introduit les nouvelles possibilités offertes par C++11 et ultérieures, nous aurions pu nous contenter de dire que "attention, c'est un vieux cours qui n'a pas été mis à jour" et que "tu sais, depuis 2011, on dispose de solutions bien plus pratiques".
Le problème, c'est qu'il raconte des conneries, qui en étaient déjà en... 1998. Et c'est ca qu'on ne peut pas laisser passer!
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Et encore, j'avais lu récemment que lmghs (il l'avait lui-même dit il me semble) avait apporté son aide pour intégrer les std::string dans le cours d'OCR il y a pas mal d' années, sinon, je pense que le cours aborderait peut-être les tableaux de type char à la place. Donc le cours serait encore plus désastreux.
Plus précisément, il y avait eu quelques fils où j'avais exprimé des critiques (au sens neutre du terme) des divers chapitres du tuto en ligne. De nombreuses ont été prises en comptes. Suite à cela, Nanoc a repris un grand nombre de choses. Et honnêtement, bravo pour le travail accompli.
D'autres choses, à mon grand regret, n'ont pas été changées (copie d'entités, pas d'initiation formelle au RAII, ou non prise en compte du C++11 qui était connu dans ses grandes lignes à l'époque de la mise à jour).
Depuis, j'ai encore affiné mes idées relativement à la pédagogie et l'enseignement du C++.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Discord NaN. Mon site.
Le Tout est souvent plus grand que la somme de ses parties.
Mon site web de jeux SDL2 entre autres : https://www.ant01.fr