La programmation en C++ moderne

Apprenez la programmation de zéro jusqu'à l'infini !

a marqué ce sujet comme résolu.

Reprise du dernier message de la page précédente

Du coup le compilateur renvoie un message d’erreur que je ne comprends pas :

Segmentation fault (core dumped)

Il n y a aucun problème … Si c’est une erreur de mal compréhension de ma part, corrigez-moi ? Encore une fois MERCI pour ce cours et courage pour la suite […]

amalure

Cette erreur peut se produire dans différentes conditions, mais ici, c’est un appel de fonctions récursives a l’infini et le programme plante quand la Pile est pleine. Donc c’est bien un bug dans le code.

Bien vu !

Je l’ai déjà dit, mais a mon sens, c’est une mauvaise pratique de montrer dans le cours de montrer la démarche : "on va écrire une fonction qui fait telle chose, voici le code", alors qu’il faudrait bien montrer explicitement et systématiquement que la démarche devrait être "on va écrire une fonction qui fait telle chose, voici les tests qui vérifient les comportements attendus, voici le code de la fonction". Les cas 0, 1 et -1 sont des cas nominaux a tester.

Édité par gbdivers

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+0 -0

Salut a tous

Hors sujet par rapport à ce cours C++ (mais pas totalement).

Je fais des lives de temps en temps sur le Twitch de NaN, pour parler de C++, de conception, etc. Il y a quelques semaines, j’ai parlé de l’idée de créer des projets d’exercices pour le C++ (et peut être Qt et GL ensuite). L’idée serait que plutôt que de donner des exercices "standalone" (donc jetables), les apprenants auraient un vrai projet et les exercices consistent à écrire des codes qui sont utilisées dans ce projet (et donc ils pourraient voir le résultat de leur travail directement dans l’application).

Par exemple, si l’exercice consiste a écrire une fonction qui calcule la position et la taille d’un bouton, ils verront dans l’UI que le bouton est bien positionné si leur code est correct.

J’ai réaliser une première version d’un tel projet, en prenant l’exemple d’un OBS stream deck (application random, pourquoi pas). J’ai présenté ce projet sur Twitch ce midi : https://www.twitch.tv/videos/624921224

Si vous avez le temps, j’aimerais avoir des retours sur ce projet, en particulier sur la facilité d’utilisation par des débutants. Et les améliorations possibles.

Édité par gbdivers

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+2 -0

Salut à tous.

Aujourd’hui, une bonne nouvelle, car @Glordim a pris en charge la validation du tutoriel. Avec votre aide et la sienne, la partie POO (en partie du moins), c’est pour très bientôt. ;)

Nous avons décidé pour l’instant de demander la validation jusqu’au chapitre sur la sémantique de déplacement. Ils nous semblent prêts, hormis corrections mineures.

Les trois chapitres suivants, sur la sémantique d’entité, le NVI et les handles, sont presque prêts car nous avons fini de les écrire, mais n’ont pas été relu attentivement, ni par nous, ni par vous. Donc si vous avez des remarques ou des idées, n’hésitez pas.


@gbdivers : j’aime bien l’idée oui. Est-ce que tu as des détails supplémentaires ? Une vision plus claire de ce que tu veux faire ?

Salut à tous.

Au final, la partie parlant du NVI ne nous plaît pas assez à cet endroit, on pense la décaler pour un peu plus tard. Donc on se retrouve avec les chapitres sur la sémantique de déplacement, d’entité et les handles qui sont prêts pour relecture. On va aussi les inclure dans la demande de validation, ce qui permettra d’offrir à tous les lecteurs un gros morceau de POO à se mettre sous la dent.

Merci beaucoup à tous pour votre aide et vos retours, ce cours prend une belle forme, on voit l’aboutissement de déjà plusieurs années d’écriture et c’est grâce à vous. Merci ! 😍


Bonjour les agrumes !

La bêta a été mise à jour et décante sa pulpe à l’adresse suivante :

Merci d’avance pour vos commentaires.

Édité par informaticienzero

Bonjour a tous , Je suis debutant en C++ et avant de m’inscrire sur ce site j’était sur le site de Openclassroom.

Serait-il possible d ajouter une barre de progression dans le cour comme sur le site de Openclassroom ?

:)

Édité par Petar

+0 -0

Staff

Bonjour a tous , Je suis debutant en C++ et avant de m’inscrire sur ce site j’était sur le site de Openclassroom.

Serait-il possible d ajouter une barre de progression dans le cour comme sur le site de Openclassroom ?

:)

Petar

Bonjour,

Pourquoi pas, vu que ça touche au site et non au cours, proposer cette suggestion sur le forum idoine ? :-)

Édité par qwerty

La tero estas nur unu lando | Géographe de service | Cliquez 👍 pour dire merci

+1 -0

bonjour, premièrement merci beaucoup pour le cours,

je sais que openclassroom n’a pas une super réputation ici, mais quoi qu’on pense du contenu de leurs cours, j’ai trouvé celui sur la programmation en C très motivant notamment grâce à des TP plus "imposants" mais aussi plus concret (petits jeux etc..)

ils permettent de bien mémoriser le cours en faisant appel à la plupart des chapitres précédents et, voir fonctionner (après moultes plantages) ses premier jeux donne vraiment envie de continuer. du coup, je pense que des TP plus concrets faisant appels aux chapitres précédents seraient vraiment un plus pour ce cours.

Édité par aperikub

+0 -0

Bonjour, dans le chapitre sur les handles, pourquoi dites vous que Serialiser_donnees est une fonction membre de serializer ?

bluescaster

C’est le chapitre II sur l’héritage, mais oui, tu as raison, c’est une coquille. La fonction dont on parle est ecrire_implementation. Bien vu. :)


PS : nous sommes heureux de vous dire que la validation est presque finie et que bientôt sera publiée la partie POO !

Édité par informaticienzero

Le taff déjà accompli est énorme. Je suis impatient de le voir publié votre cours. Félicitations.

Et bon courage pour le terminer, je sais que c’est beaucoup de travail !

+1 -0

Hello.

Désolé pour ma relecture très tardive. D’autant, que je pose des questions pas si triviales. ^^'

Je passe en coup de vent car je me posais une question non triviale relativement à des aspects de progression pédagogique. Est-ce que l’on peut présenter la sémantique de déplacement toute seule, ou est-ce un sujet advanced pas pour débutants?

On a deux grandes applications:

  • l’optim qui cherche à réduire les co^uts (désolé, je ne n’ai toujours pas trouvé comment configurer correctement les touches mortes de mon clavier) quand on ne peut pas profiter d’une élision de copie
  • les transferts de responsabilités sur les handles, à commencer par unique_ptr. Indirectement, est-ce qu’il ne faudrait pas présenter comment unique_ptr s’utilise avant de parler de la sémantique de déplacement?

A propos de ce chapitre, une nouvelle relecture.

un simple tableau d’entier

"entier" devrait prendre la marque du pluriel.


Matrice(Matrice && matrice) noexcept = default;

Hum… Est-ce qu’il ne faudrait pas rappeler que si ce code ici ne plante pas c’est parce que tous les attributs de la matrice sont déplaçables sans fragiliser d’invariants du genre: "un seul responsable des libérations". Et là, un joli piège: il y un invariant dans la matrice: buffer.size() == nbligs * nbcols… Donc, il faut bien définir les 2 opérations de déplacement.


Les rvalues, acronymes de right-handed values, désignent […] mais aussi les objets qui arrivent en fin de vie, comme le temporaire m1 + m2 qu’on a vu en exemple dans la section précédente.

J’aime bien résumer ces temporaires en "plus généralement: des expressions". Ca distingue assez bien de "variable" je trouve. D’ailleurs à la lecture, étrange. "expression" est le terme que je réserve aux rvalues pour employer "variable (ou expression [OK, expression n’est pas si faux] qui s’apparente à une variable)" pour les lvalues.


pile & tas: La portion qu’utilise le programme est découpée en deux grandes zones.

En vrai, il y a le static storage aussi. Je dirais plut^ot: "intéressons-nous à deux des grandes zones de la mémoire vive qu’un programme est amené à manipuler", par exemple.


Ainsi, quand on créé un tableau redimensionnable avec std::vector, ou qu’on manipule un fichier avec std::fstream, les données sont stockées dans le tas 2

Euh… pour les flux, ce n’est pas une propriété première. Au mieux, c’est un détail d’implémentation relativement aux mises en cache avant flush.


la libération doit aussi être explicite.

Quand on décide d’allouer à la main, hein?!


Si l’attribut est stocké sur le tas

Je n’aime pas ce raccourci. L’attribut est toujours dans l’espace de son englobant. Maintenant certains attributs sont en fait des identifiants (handles, adresses mémoire) qui font référence à des zones ailleurs (tas, sockets, fichiers). Un peu comme une fiche de livre dans une bibliothèque: déplacer la fiche ne déplace pas le livre, le numéro du livre se retrouve sur la fiche qui est rangée ailleurs (dans le casier des emprunts p.ex.).

Peut-^etre faudrait-il introduire les termes des notions de copie superficielle (shallow) et de copie profonde (deep)? Et donc la copie des numériques (vrais nombres, ou handles/adresses) est toujours superficielle, tout comme leur déplacement qui est en fait une copie en C++ : pour des raisons de performance, il n’y a pas de remise à zéro.

C’est à nous de coller une sémantique de déplacement sur nos handles en les enrichissant.

Ca mériterait un dessin dès maintenant.


La sémantique de déplacement ne garantie donc pas l’absence de copie. Si l’on n’a que des attributs qui utilisent la pile (comme Fraction), on ne gagne rien par rapport à la copie. Par contre, dès lors qu’il y a des attributs qui utilisent le tas (comme std::vector ou std::string), on peut gagner en performance.

Ce n’est pas tant une question de pile et de tas, mais de ce que représente l’attribut. Si on met de c^oté les attributs à qui nous avons collé une sémantique de déplacement ou qui en obtiennent une de leurs attributs & parents (NB: cela ne règle pas le cas de l’invariant multi-attributs des matrices, des tableaux & chaines…), la question devient plut^ot est-ce que l’attribut ne serait pas un machin (genre un entier, un ISBN…) qui sert à identifier un truc qui vit sa vie ailleurs (RAM/fichier/socket, livre…).

Alors dans ce cas, il peut y avoir gain à déplacer. Autrement, toute la charge utile de l’objet englobant se trouve ^etre uniquement dans cet englobant.

Dites-moi si je ne suis pas clair.


PS:

struct S {
    std::array<int, 42> t;
};

auto s = new S;

s->t est bien dans le tas et pourtant new S(std::move(*s)) reviendra à une copie.

+1 -0

Je passe en coup de vent car je me posais une question non triviale relativement à des aspects de progression pédagogique. Est-ce que l’on peut présenter la sémantique de déplacement toute seule, ou est-ce un sujet advanced pas pour débutants?

On a deux grandes applications:

  • l’optim qui cherche à réduire les co^uts (désolé, je ne n’ai toujours pas trouvé comment configurer correctement les touches mortes de mon clavier) quand on ne peut pas profiter d’une élision de copie
  • les transferts de responsabilités sur les handles, à commencer par unique_ptr. Indirectement, est-ce qu’il ne faudrait pas présenter comment unique_ptr s’utilise avant de parler de la sémantique de déplacement?
lmghs

Dans mon cours, je parlais beaucoup plus tôt des déplacements, donc je dirais que c’est pas trop avancé pour les débutants. Cela va dépendre de la pédagogie.

Je suivais la logique suivante :

  • apprendre la sémantique de haut niveau avant de s’intéresser aux détails internes
  • donc apprendre à utiliser avant d’apprendre à implémenter

Selon cette logique, je dirais qu’aborder le déplacement dans la partie POO est très (trop) tard. Si on considère l’utilisation du déplacement, je pense qu’on peut apprendre à l’utiliser dans les changements de contexte, c’est à dire lors des appels de fonctions.

En termes d’explications, je pense qu’il est possible de faire simple, en se focalisant sur la sémantique. 3 schémas pour présenter les 3 concepts importants dans les appels de fonction (à mon avis, ces schémas sont suffisant pour comprendre les explications à donner aux apprenants) :

J’utilisais int et std::vector pour les explications. Ca pourrait etre bien de présenter aussi avec une classe non copiable comme std::unique_ptr, mais comme j’avais choisit de parler des pointeurs qu’au moment du polymorphisme d’héritage dans mon cours, je ne pouvais pas l’utiliser comme exemple pour le chapitre sur le déplacement.

Par contre :

l’optim qui cherche à réduire les co^uts

Je suis pas mal réticent a parler de performances aux débutants. Ils aiment se jeter sur la moindre optimisation, même quand ce n’est pas pertinent. Parler de sémantique en premier me semble plus pertinent.

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+0 -0

l’optim qui cherche à réduire les coûts

Je suis pas mal réticent a parler de performances aux débutants. Ils aiment se jeter sur la moindre optimisation, même quand ce n’est pas pertinent. Parler de sémantique en premier me semble plus pertinent.

Je suis d’accord. Je n’ai pas développé plus mon pavé car justement: l’optim n’est pas un sujet de débutants, et à ce sujet, l’élision de copie me parait limite plus pertinente en premier avant si c’est l’axe (optim) avec lequel le sujet (move) devait être présenté. Bref, c’est le moment auquel le sujet est présenté qui me fait tiquer, car il pourrait être perçu comme "optimisons nos matrices". Et j’ai peur que cela soit un mélange de mauvais message, et/ou message complexe à ce stade là.

Et j’étais gêné pour parler des unique_ptr et autres handles avant, car cela introduit des notions un chouilla avancé. Peut-etre juste rester avec des handles très huile de coude comme la fiche de livre à laquelle je pensais avant de faire le saut fiche/id (ou valise de biftons) —> adresse. Ou alors je me complique trop la vie avec ces questions.

+0 -0

§ Entités & héritage

Exemples avec des Personnes

Je ne suis pas très fan de ces exemples, car on a vite fait d’enchaîner sur de l’héritage qui rajoute des choses (et en général concrètes et non fonctionnelles), plutôt que de l’héritage qui spécialise des comportements.

Après, ils sont correctement employés, et il est vrai que l’héritage permet aussi de rajouter des choses.

Une question de visibilité

Je ne peux pas accéder aux éléments privées d’une classe mère dans une classe fille

Plutôt que "dans", je préfère "depuis".


LSP

En autre exemple sur le sujet, j’aime bien prendre les listes et les listes triées. C’est un exemple informatique, potentiellement plus simple à accepter. Par expérience, il y a toujours des gens qui vont chipoter avec l’exemple des carrés & rectangles, à commencer par ceux qui vont introduire en plus du slicing volontairement. Brrrr.

A propos des carré et rectangle, je rajoute la petite note: "c’est quoi la différence avec les maths? En maths, un carré n’a pas de vie propre: ce n’est pas un objet qui bouge, grossit…"


Masquage de fonctions.

Il y a une longue tradition en C++ de commencer par montrer l’erreur du masquage. D’un point de vue pédagogique, il y a un petit coté qui me perturbe car ce n’est pas ce dont on a besoin, mais pourtant on commence toujours par ces fichus pointeurs euuuh. Pardon, je voulais dire masquage. Vous voyez mon sous-entendu.

Le problème est que l’erreur est vite arrivée en C++. J’imagine que c’est pourquoi on commence par elle.

Bref. Tout ça pour en arriver à la petit note à laquelle je pensais. Ailleurs, j’ai vu une comparaison à Java et Python. J’aurai tendance à en refaire une ici: "Toute liaison est complètement dynamique en Python (il faut bien s’enquiquiner pour avoir du final). Par défaut c’est également dynamique en Java, mais C# a fait le même choix que celui fait par le C++: statique sauf spécification explicite contraire. "


Defs de redéfinir et supplanter:

"redéfinir" est la traduction courante de "to override" dans le contexte des langages OO. C’est le terme officiel si je puis dire. "Supplanter" est une traduction alternative non officielle. Cependant, quand j’explique ce que fait une "redéfinition", je le fais avec le terme "supplanter" car je le trouve plus explicite en français pour décrire ce qu’il se passe.

Techniquement, il n’y a pas de différence entre les deux.

Donner une nouvelle définition à une fonction membre non virtuelle (qui existe dans une classe parente) dans une classe fille, c’est officiellement un "masquage de fonction". Officiellement j’ai vu de nombreuses personnes considérer que ce n’est pas une surcharge — peut-être parce que tous les symboles ne sont pas utilisables en même temps? Perso jusqu’à présent je range ça dans les surcharges, je suis un hérétique.


Polymorphisme d’inclusion

NB: Si le terme complet est "Polymorphisme d’inclusion" ou "… d’héritage", dans le monde OO, on dit juste "Polymorphisme". Ailleurs juste "polymorphisme" va en désigner un autre.


au sujet des contrats et du LSP.

C’est un sujet qui peut paraître abstrait et nous passer complètement au dessus les premiers temps. Ce qu’il faut retenir dans une première lecture, c’est qu’un héritage public qui ne respecte pas ces contrats a des chances non négligeables d’être la source de comportements erronés qui seront très complexes à corriger. Généralement cela ne survient pas de suite, mais quand on commence à faire dialoguer des morceaux différents qui marchaient très bien chacun dans leur coin avec leurs amis respectifs, mais qui se révèlent incapables de travailler ensembles.

+1 -0

Merci à tous pour vos retours.

Je suis sur téléphone donc je vais pas pouvoir tout détailler. Mais j’aimerais revenir sur la sémantique de mouvement.

Je ne suis pas un débutant C++, mais je n’ai clairement pas autant d’expérience que vous. Par contre, je sais que dans d’autres langages, ces notions de références, de déplacements, copies, etc sont bien souvent masquées. Je n’ai compris qu’assez tard l’intérêt de la sémantique de mouvement (bon après C++ est un langage complexe, on va pas se mentir).

Du coup, en réfléchissant au plan, j’ai du mal à voir comment l’introduire plus tôt dans le cours sans que ça soit rébarbatif ou trop abstrait. C’est peut-être mon manque de recul sur cette notion.

D’un autre côté, est-ce qu’un débutant à vraiment un intérêt à en savoir plus que le std::move avec les handles ? Parce que c’est la seule raison pour laquelle on l’utilise dans ce cours. Comme en plus il existe un tutoriel à part sur le sujet, on peut même envisager de ne pas en parler dans le cours et de se contenter de mettre le tutoriel de @mehdidou99 en lecture post-tutoriel. Surtout qu’un tutoriel à part aurait le mérite de pouvoir aller plus loin, plus en détail.

De toute façon, le tutoriel n’a pas vocation à tout couvrir non plus. Il faut faire des choix.

C’est une question ouverte, qu’en pensez-vous ?

Le move() sur les handles me parait suffisant aussi à la première réflexion pour effectivement avoir un chapitre/tuto hardcore du genre "la sémantique de valeur… à l’huile de coude", et peut-être un avant "la sémantique de valeur… pour les nuls" où l’on aborde le déplacement de gros paramètres — toujours bâtis autour de types standards déplaçables.

Maintenant, il y a possiblement les aspects de ce que permet de catcher une rvref. Mais a-t-on besoin de détailler? Je ne sais pas.

Quant une préintro, gbdivers en parle, mais je n’en mesure pas encore les impacts.

+0 -0

Je ne suis pas un débutant C++, mais je n’ai clairement pas autant d’expérience que vous. Par contre, je sais que dans d’autres langages, ces notions de références, de déplacements, copies, etc sont bien souvent masquées. Je n’ai compris qu’assez tard l’intérêt de la sémantique de mouvement (bon après C++ est un langage complexe, on va pas se mentir).

Du coup, en réfléchissant au plan, j’ai du mal à voir comment l’introduire plus tôt dans le cours sans que ça soit rébarbatif ou trop abstrait. C’est peut-être mon manque de recul sur cette notion.

informaticienzero

Je ne l’ai pas précisé dans mon message, mais j’ai suivi une démarche différente dans mon cours, donc je ne sais pas si mes remarques sont applicables dans le votre. A vous de voir.

Pour expliquer, retour aux fondamentaux : "pourquoi ?". Pourquoi on parle du déplacement ? Pour la copie ? Les pointeurs ? Pourquoi ces choix en C++ et pas dans les autres langages ?

La base, c’est la qualité logicielle. On veut du code de qualité, c’est a dire sans bug. Un point important pour ne pas avoir de bugs, c’est que les objets qu’on utilise soient valides. Donc qu’ils soient initialisés et détruits et qu’on ne les utilisent pas avant leur initialisation, pas après leur destruction. Dans les autres langages, la gestion des objets peut etre totalement masquée et donc la validité est garantie en permanence. Pas en C++. C’est le premier point : la spécificité du C++ par rapport a la gestion des objets.

Ces "phases" de vie des objets permettent d’introduire 2 notions fondamentales (a mon sens) : l’ownership (qui est responsable de détruire un objet) et le lifetime (quand un objet est construit et détruit).

La copie, le déplacement, les indirections, le RAII, etc. ne sont que des outils pour appliquer concrètement ces 2 concepts. Mais c’est bien la compréhension de ces 2 notions qui est importante et qui est l’objectif a atteindre pour l’apprenant.

Je pense que introduit comme cela, il est relativement facile d’en parler dans un cours débutant, sans être abstrait ou rébarbatif.

Édité par gbdivers

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+1 -0

Salut à tous.

Du coup, suite aux retours de @lmghs, on a mis à jour le chapitre sur la sémantique d’entité.

Je réfléchis encore à la sémantique de mouvement, parce que les arguments sont bons des deux côtés et c’est difficile de faire un choix. :)


Bonjour les agrumes !

La bêta a été mise à jour et décante sa pulpe à l’adresse suivante :

Merci d’avance pour vos commentaires.

Édité par informaticienzero

Salut à tous.

Je viens de mettre à jour le chapitre sur les handles. Je continue toujours à réfléchir à la sémantique de mouvement.


Bonjour les agrumes !

La bêta a été mise à jour et décante sa pulpe à l’adresse suivante :

Merci d’avance pour vos commentaires.

Édité par informaticienzero

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