La programmation en C++ moderne

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

a marqué ce sujet comme résolu.

@Dedeun : ton aide et tes remarques sont plus que bienvenues. :)

Oui, on pourrait faire une matrice unitaire. Reste que ça peut être une complication de plus pour ceux qui ne sont pas trop amis avec les maths, sans vraiment ajouter de valeur à l’exercice, le but étant vraiment de pratiquer la sémantique de valeur.

Mais après, rien n’empêche un lecteur d’aller plus loin. Au contraire, on l’y encourage très fortement. :)

Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.


Salut à tous,

Voici une mise à jour du cours, avec une partie sur le copy-and-swap étoffée. Il me reste ensuite le TP à finir et on pourra valider ces trois premiers chapitres.

@lmghs : que veux-tu dire par duplication ? À quelle partie du cours fais-tu références ?

Pour Doxygen, j’ai corrigé. À force de faire du Phpdoc j’ai confondu et oui, avec Doxygen, pas de type de retour à préciser. Alors que Phpdoc oui, d’où la confusion. :)

Hello.

Dupliquer == copier => constructeur de copie et opérateur d’affectation. C’est le § sur la sémantique de valeur, avec un exemple qui ne justifie pas d’entrer dans tous ces détails. Est-ce qu’il ne faut pas l’aborder en deux fois du coup pour un aspect pédagogique: juste dire dans un premier temps qu’il y a des copies, et que c’est transparent. Faire écrire une fraction ou autre classe mathématique simple.

Et puis, un second chapitre "la copie pour les initiés" qui prend p.ex une classe polynôme en exemple (basée sur un vecteur), puis le TP sur la matrice. Une idée comme ça. Et on peut encore envisagé un chapitre (plus loin?) : "la copie pour les experts (/grands?)" qui cette fois utilise un unique_ptr, voire plus tard (anexes?) un raw pointeur pour bien montrer les diverses possibilités pour s’y prendre correctement.

PS: il y a depuis hier un troll sur reddit qui parle de C++ en premier langage. Il y a eu 2–3 idée intéressantes de choses à ne pas montrer au début.

PPS: chez moi la matrice est un "TP fil rouge", qui se complexifie au fil des chapitres". D’abord la sntaxe, puis la mémoire, puis le déplacement, puis les templates, voire les expression templates pour les sessions "pour (wanabe) experts".

Petite mise à jour au passage, j’ai retiré la partie copy and swap, parce que je ne vois pas à ce stade comment l’introduire simplement et de façon utile. Je décale à plus tard, comme @lmghs a dit à un moment quand des ressources pourront nous casser les pieds (pointeur, fichier, etc).

Du coup, il reste à écrire la correction du TP et on est bon pour une première validation. :)

PS: il y a depuis hier un troll sur reddit qui parle de C++ en premier langage. Il y a eu 2–3 idée intéressantes de choses à ne pas montrer au début.

Tu nous fais un resume TL;DR ?

gbdivers

Sur https://old.reddit.com/r/cpp/comments/ep1s2i/what_is_this_communities_opinion_on_c11_as_a/

Beaucoup de same ol' same ol' avec: C d’abord enseigne comment marche un ordi, et bien des trolls.

Dans les choses plus intéressantes peu souvent abordées entre nous:

Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.


Salut à tous.

J’ai fini la correction du TP. Je vais ajouter quelques tests, mais sinon c’est bon. Je vais donc envoyer les trois premiers chapitres en validation. N’hésitez néanmoins pas à faire des retours et des remarques, ce n’est jamais trop tard. Merci à tous pour votre aide. ;)

Le TP

corps et âmes

âme sera au singulier.

opérateur ()

Peut-être indiquer que que grâce à lui, le compilateur interprètera obj(i,j) en obj.operator()(i,j)?

#include <iostream>

Je range ce fichier dans les fausse bonnes idées. Surtout pour un fichier d’en-tête. Ici, c’est <iosfwd> qu’il faudrait dans le .h, et <ostream> dans le .cpp (pourquoi charger la déclaration des variables globales std::cout et cie?). Maintenant, <iosfwd> est possiblement un topic plus avancé et on peut se contenter d'<ostream> à la place.

lignes

n’est pas un bon nom de variables pour un nombre

_whatever

Vade Retro Pythanas! https://cpp.developpez.com/faq/cpp/?page=Norme#Quels-sont-les-identificateurs-interdits-par-la-norme

defaulted copy-constructor

Ce pourrait être une fausse bonne idée car, si je ne m’abuse, elle a pour conséquence de désactiver le constructeur par déplacement. Ici, c’est pas plus mal car il faudrait faire un RAZ des autres attributs pour garder une consistance avec le buffer interne.

m1, m2

En général, je préfère la convention Left Hand Side (lhs), et Right Hand Side (rhs).

friend Matrice operator*(Matrice m, int multiplicateur);

Il manque le symétrique

friend Matrice operator*(Matrice m1, Matrice const & m2);

On ne peut pas implémenter mat1 * mat2 à partir de *= — c’est le même problème avec des polynomes. Du coup, il est plus juste de recevoir une référence constante ici.

L’utilisation d’une copie en interne montre bien que c’est maladroit. Mieux, vaut définir *= à partir de * pour une rare fois.

Matrice transposition() const;

Les verbes sont préférables pour des fonctions qui ne sont pas des questions -> transpose()

assert(_lignes > 0 && _colonnes > 0 && "On ne peut pas avoir une matrice de dimension 0.");

En fait si. Après un déplacement. C’est n’est qu’avec je ne sais plus quelle feinte avancée (cf article de Andrzj) du C++17 que l’on peut avoir oublié l’état purgé vide. Mais pour une classe valeur, je ne suis pas sûr que cela soit réaliste. => il faut abandonner l’invariant non-vide.

operator()(i, j) += matrice(i, j);

Je préfère (*this)(i,j) — je me demande si je n’ai pas vu passer une proposition qui vise à bloquer v.operator¤(args) (¤ == placeholder).

Bref, il faut exploiter la structure linéaire et faire une seule boucle, voire utiliser l’algo standard qui va bien — s’il y en a un, d’un coup je ne sais plus.

return m1 += m2;

C’est une fausse bonne idée qui bloque une optim si mes souvenirs sont bons. => lhs += rhs; return lhs; Ou alors, c’est quand on commence à être précis avec des rv-ref et des const-ref??? J’ai oublié.

std::endl

-> '\n'

Matrice::ligne

S’implémente avec std::copy_n

De la définition des opérateurs

Peut-être faut-il anticiper les cas classiques qui sont

struct S {
    S operator+(S, S);        // Erreur
    S operator+(S);           // Maladresse, AMA
    friend S operator+(S);    // Erreur
};

// Cas juste, qui surcharge la liste des résolutions possibles, et donc 
// les messages d'erreur interminables
/*pas friend*/ S operator+(S, S);

J’y ai droit à chaque fois en TP. Et c’est un problème de comprehension récurrent sur le tudo du sdz/oc.

Salut. J’ai commencé à stream sur le twitch de NaN pour faire de la rédaction/relecture de tutos en live. En particulier pour le cours C++. https://www.twitch.tv/not_a_name42/

La Programmation Orientée Objet

Nous avons notamment vu les paradigmes impératif, fonctionnel et générique.

Il faudrait vérifier la déf de prog fonctionnelle, mais je ne me souviens pas que le début du cours aborde réellement cela. A vérifier.

Mais il y en a un que l’on a laissé de côté

Phrase peut etre a simplfier?

P.O.O

C’est inutilement lourd de mettre les points. Du coup, POO ? (Et si on veut mettre réellement les points, alors c’est P.O.O.)

permet une conception d’un tout autre niveau en termes d’élégance, d’expressivité, de robustesse

Je ne me souviens plus, le tuto parle de qualité logiciel dans les premières parties ? https://fr.wikipedia.org/wiki/Qualit%C3%A9_logicielle

Je

C’est qui le "je" sur les 2 auteurs ? :) (je chipote)

Or le C++

Or, le C++ ?

Premiers pas avec la P.O.O

Liste des objectifs du chapitre au début ?

Je ne vais pas trop faire des remarques sur les tournures de phrases, mais à mon avis il est possible d’améliorer ça. Je relis le cours en live à voix haute et certaines phrases sont lourdes à lire. Je vous conseille de faire cette relecture à voix haute, ça aide à voir les tournures un peu lourde.

Ainsi, dans le code std::string chaine, chaine n’est ni plus ni moins qu’un objet de type std::string. De même pour int x, où notre variable x est un objet de type int.

Peut être pas clair la distinction entre "variable" et "objet". Peut être rappeler la définition de "variable".

En fonction du type d’un objet, on peut faire plus ou moins de choses, lui appliquer plus ou moins de services

On applique pas des services sur l’objet, c’est l’objet qui rend des services.

Ce terme « fonction membre » est à mettre en relation avec « fonction libre », terme qui désigne les fonctions qui ne dépendent pas d’un objet.

Je ne suis pas sur de "qui ne dépendent pas d’un objet". Si on écrit :

s.size() // fonction membre = qui dépend d'un objet
std::size(s) // fonction libre = qui dépend aussi d'un objet ?

Définir les services comprend d’autres choses supplémentaire, en plus des fonctions membres

Pas sur de voir ce que sont ces "autres choses" qui interviennent dans la définition des services et qui ne sont pas des fonctions membres.

struct Fraction
{
    int numerateur;
    int denominateur;
};

L’initialisation par défaut des membres n’a pas été vu ? C’est pas conseillé comme bonne pratique ?

Fraction f1 { 5, 2 };

Je suppose que vous avez expliqué la bonne pratique de mettre const par défaut. Il faut peut etre une explication ici pour préciser pourquoi cette variable n’est pas const ?

cette variable est à considérer comme un réel

Pas sur que "considérer" soit correcte, puisqu’il y a réellement création d’un objet de type double en mémoire, avec utilisation du CPU pour cette conversion. C’est pas un simple "dire au compilateur de considérer que le type est différent" comme on a dans un upcasting.

double Fraction::valeur_reelle()

Peut être préciser cet opérateur :: (déja vu par le lecture pour les espaces de noms).

numerateur /= pgcd;
denominateur /= pgcd;

Dans la doc de "gcd" : "If both m and n are zero, returns zero". Si "pgcd" peut etre nul, il faut un assert ou un if.

J’arrête la pour ce soir, il est tard pour moi. j’ai fait des commentaires en live sur d’autres points, mais je reviendrai dessus un autre jour.

+2 -0

Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.


Salut à tous.

Merci @lmghs et @gbdivers pour vos retours, je les ai quasiment tous pris en compte dans cette mise à jour de la bêta. Je réponds brièvement à quelques points seulement (boulot), je repasse plus tard pour compléter.

@lmghs : concernant les _whatever, effectivement, faute de ma part et déformation professionnelle. Je ne suis pas extrèmement fan, mais on va partir sur du m_, comme beaucoup le font en C++.

J’ai pris beaucoup de tes remarques en compte, il en reste quelques-unes encore (expliquer le coup de (), le std::copy_n). Je veux bien un mot sur le paragraphe De la définition des opérateurs s’il-te-plaît. Les explications du cours ne sont pas bonnes ? Ou c’est le code du TP qui pose problème ?

PS : je n’ai pas mis la correction à jour, seulement le code.

@gbdivers : concernant l’initialisation par défaut, bien vu, je n’y ai pas réfléchi. Pour std::gcd, vu que la fraction ne peut exister que si m_denominateur != 0, est-ce qu’on peut quand même tomber dans le cas où on obtient 0 en retour ?

Pour la vidéo, y’a t-il un replay ? Ou bien est-ce perdu à jamais ? :(

Merci encore pour votre aide !

Je veux bien un mot sur le paragraphe De la définition des opérateurs s’il-te-plaît. Les explications du cours ne sont pas bonnes ? Ou c’est le code du TP qui pose problème ?

La question que je me pose est: faut-il expliquer pourquoi une autre solution qu’il ont pu tester ne fonctionnait pas ou n’était pas celle de la correction? C’est ce que je fais en présentiel. Mais en totalement isolé comme ici, comment peut-on assurer ce feedback sur un point qui n’est pas si trivial pour tout le monde?

vu que la fraction ne peut exister que si m_denominateur != 0, est-ce qu’on peut quand même tomber dans le cas où on obtient 0 en retour ?

Pour la vidéo, y’a t-il un replay ? Ou bien est-ce perdu à jamais ? :(

Merci encore pour votre aide !

informaticienzero

Non. Vu la definition de la structure, m_denominateur != 0 ne fait pas partie des invariants de classe. Donc rien n’interdit d’écrire :

Fraction f { 0, 0 };

Ce qui compte, c’est pas comment une fraction est définie mathématiquement, mais comment la classe est définie dans le code.

De toute facon, une fraction qui aura le numérateur nul aura le même problème.

Je suppose que vous avez expliqué la bonne pratique de mettre const par défaut. Il faut peut etre une explication ici pour préciser pourquoi cette variable n’est pas const ?

#include <iostream>

struct Fraction
{
    double valeur_reelle();
    int numerateur;
    int denominateur;
};

double Fraction::valeur_reelle()
{
    return static_cast<double>(numerateur) / denominateur;
}

int main()
{
    Fraction const f1 { 5, 2 };
    std::cout << "5/2 = " << f1.valeur_reelle() << std::endl;
    return 0;
}

Il y avait un piège dans ma remarque :)

Si f1 est const, alors valeur_reelle() doit aussi l’etre. Le code tel qu’il est donné ne compile pas.

Affreux dilemme : si vous avez dit que vos variables doivent etre const par defaut, alors il faut soit expliquer pourquoi f1 ne l’est pas, soit mettre valeur_reelle() const et l’expliquer ce const.

Pour la vidéo, y’a t-il un replay ? Ou bien est-ce perdu à jamais ?

Pour le moment, c’est sur mon twitch https://www.twitch.tv/guillaumebelz/videos mais ca devrait aller sur le twtich de NaN par la suite.

+0 -0

@gbdivers : oui, bien vu, j’ai oublié qu’on est dans le cas de la structure et qu’il n’y pas encore l’invariant m_denominateur != 0 dans le constructeur. Je vois mieux le problème. :)

Il y avait un piège dans ma remarque :)

Si f1 est const, alors valeur_reelle() doit aussi l’etre. Le code tel qu’il est donné ne compile pas.

Affreux dilemme : si vous avez dit que vos variables doivent etre const par defaut, alors il faut soit expliquer pourquoi f1 ne l’est pas, soit mettre valeur_reelle() const et l’expliquer ce const.

gbdivers

Oui, c’est exactement ce que je me suis dit. Sauf que j’ai fais un copier-coller foireux. Du coup, je pense rajouter un simple encadré disant qu’on ne met pas const tout de suite et que les explications viennent après. C’est comme ça que j’introduis ça dans la section dédiée.

Revenons à un mot-clé que nous connaissons depuis longtemps, que l’on utilise beaucoup mais que j’ai volontairement laissé de côté depuis l’introduction à la POO. Il s’agit de const. Il nous permet de déclarer qu’un objet est constant et ainsi empêcher d’éventuelles modifications. Sauf que si on déclare notre objet Fraction comme étant const, ça ne compile plus.

Dernier point : je ne vois que deux vidéos sur la configuration et l’installation de Qt. Et pour les deux, pas de son.

Le son de certaines parties de cette vidéo a été désactivé parce qu’il semblerait que son contenu soit soumis à des droits d’auteur appartenant à ou contrôlés par un tiers.

Dernier point, j’ai le droit à un avertissement comme quoi ce sont des vidéos pour adultes. C’est normal ? :P

La suite de ma relecture.

Encapsulation et invariants

je m’arroge

Mot compliqué moi pas comprendre.

Ici, j’ai effectuée une opération mathématiquement impossible et mon programme est donc invalide.

Il n’est pas "invalide" dans l’absolu. Au pire, il est ou il n’est pas conforme aux attentes/besoins spécifiés.

En termes de démarche, c’est important, parce qu’un programme n’est pas invalide parce qu’il va manipuler une valeur ou faire un calcul qui n’a mathématiquement aucun sens (et encore, cela va dépendre du niveau de connaissance en math), mais parce qu’il ne va pas passer les tests. La démarche est :

  • on définie un comportement attendu (ce que le programme devra faire et ne pas faire)
  • on traduit ce comportement en tests (si on fait du TDD) ou des preuves (pour faire plaisir à Peuk)
  • le programme sera valide s’il passe les tests et invalide s’il ne les passe pas. C’est ça, le critère de validité.

Il faut (à mon avis) que cette démarche soit progressivement assimilées pendant toute la durée du cours.

D’ailleurs, je reviens sur l’exo :

Et si vous vous entraîniez, en implémentant un nouveau service ? Ce serait une bonne chose que de pouvoir simplifier la fraction, c’est-à-dire avoir le numérateur et le dénominateur les plus petits possibles.

J’en parle plus longuement sur le live, mais donner juste le code, c’est ne pas montrer le bon exemple en termes de ce qu’on (je ?) attend comme travail. Si on veut que l’apprenant ne se lance pas directement dans le code, mais suis une méthodo (1. def des besoins. 2. tests. 3. def de l’API + les tests échouent. 4. Implementation + les tests passent. doc, deploiment, etc), il faut "montrer l’exemple" dans les solutions d’exercice proposées et montrer explicitement cette démarche.

Sinon, on n’apprend que la syntaxe et on perd beaucoup de la programmation "moderne".

Le problème, c’est que j’ai violé l’invariant de ma classe.

Remarque idiote : cet invariant a été défini avant ? A priori, non, donc il ne peut pas être violé ! ;)

D’où l’importance de la démarche (définir puis implémenter). On pourrait tout a fait décider que notre classe Fraction supporte Inf et dans cas, il n’y a pas de violation de l’invariant. Cela n’a pas de sens de dire que l’invariant est violé s’il n’est pas encore défini.

Un invariant c’est quoi ? Un ensemble de garanties sur l’état de toutes les instances de ma classe. Tant que cet invariant tient toujours, mes classes sont supposées rendre les services qu’elles exposent. Par contre, s’il vient à être violé, alors tout peut arriver. C’est typiquement de la programmation par contrat.

Je ne me souviens pas, la prog par contrat a été abordée dans la partie fonction ? (pré et post conditions)

alors tout peut arriver

Cette formulation me gene mais je ne sais pas exactement par quoi la remplacer.

Son invariant est

Idem pour le "est". La formulation me gène un peu. Cf le live, je ne sais pas trop comment expliquer (il est tard…)

Nous ne laisserons plus que les services à la vue des autres, étant donnés que eux savent quels sont les invariants à respecter.

Qui "eux" ?

L’astuce consiste à remplacer le mot-clé struct par class.

Maladroit.

Ils peuvent être publics et accessibles depuis le reste du monde, par tout le monde. Ou bien ils peuvent être privés et seule la classe les déclarant peut les manipuler.

Ajouter une petite note pour dire qu’il existe une troisième visibilité et que ça sera vu plus tard.

La visibilité a déjà été vu avant ?

Notre code contient quand même un dernier problème, bloquant toujours la compilation.

Donnez l’erreur de compilation. C’est important pour que l’apprenant apprenne aussi les messages d’erreur de compilation.

pour qu’il soit dans un état cohérent et utilisable

C’est quoi un "état cohérent" ?

En effet, nous ne pouvons plus initialiser notre objet.

Pas totalement vrai. On peut l’initialiser par défaut.

Fraction f1;

C’est ici que seront faites les initialisations de nos différents attributs, entres autres.

"les initialisations avec des valeurs". Les initialisations par defaut des membres devrait (je pense) etre faites lors de la déclaration des membres.

(Et où sont les initialisations par défaut :) )

prog.cc:23:5: error: use of undeclared identifier 'numerateur'; did you mean 'm_numerateur'?
    numerateur /= pgcd;
    ^~~~~~~~~~
    m_numerateur
prog.cc:11:9: note: 'm_numerateur' declared here
    int m_numerateur;
        ^
prog.cc:24:5: error: use of undeclared identifier 'denominateur'; did you mean 'm_denominateur'?
    denominateur /= pgcd;
    ^~~~~~~~~~~~
    m_denominateur
prog.cc:12:9: note: 'm_denominateur' declared here
    int m_denominateur;
        ^

Hihihi.

Plus sérieusement, il serait possible de mettre le code dans un github + build automatique + génération automatique du cours avec le code provenant de github ?

On appelle cette syntaxe la liste d’initialisation.

J’aime bien que les termes anglais soit donné (ici "member initializer lists". Peut etre qu’il faudrait dire "liste d’initialisation des membres").

le constructeur d’une classe peut être surchargé

La notion de surcharge a déjà été vu dans le cours ?

on aura ici un peu plus de travail.

Si le cours a déja abordé les chaines et les conversions numériques, ca pourrait etre un code a donner en exercice.

Un dernier invariant pour la route

Le donner en exo

Dans ce genre de cas, il est plus pratique d’écrire l’implémentation de ce service directement dans le fichier d’en-tête.

Pas fan de cette regle.

Le constructeur permet d’initialiser l’objet, tout en vérifiant que les valeurs fournies sont correctes et ne risquent pas de violer les invariants.

Peut etre que ca serait pertinent de parle du constructeur par defaut avant de parler des constructeurs avec paramètres.

Toutes les méthodes qui ne modifient pas l’objet doivent être déclarée const.

Ca laisse sous entendre qu’on écrit la fonction et après qu’on décide si elle est const ou pas. La méthodo devrait etre l’inverse.


La fois prochaine, je relirais le chapitre "Une classe de grande valeur".

+2 -0

Ici, j’ai effectuée une opération mathématiquement impossible et mon programme est donc invalide.

Il n’est pas "invalide" dans l’absolu. Au pire, il est ou il n’est pas conforme aux attentes/besoins spécifiés.

En termes de démarche, c’est important, parce qu’un programme n’est pas invalide parce qu’il va manipuler une valeur ou faire un calcul qui n’a mathématiquement aucun sens (et encore, cela va dépendre du niveau de connaissance en math), mais parce qu’il ne va pas passer les tests.

Je plussoie ça. Pour être plus précis, on a deux sortes de validité pour un programme :

  • la validité par rapport à la sémantique du langage,
  • la validité par rapport à la sémantique que l’on souhaite donner à notre programme.

La première doit déjà avoir été inculquée plus tôt dans le cours (et il y a encore des éléments en rapport aux éléments techniques que vous inroduisez encore à cette étape), mais sans définition de "ce que je veux", on ne peut pas dire si ce que fait le programme est valide ou invalide.

Un invariant c’est quoi ? Un ensemble de garanties sur l’état de toutes les instances de ma classe. Tant que cet invariant tient toujours, mes classes sont supposées rendre les services qu’elles exposent. Par contre, s’il vient à être violé, alors tout peut arriver. C’est typiquement de la programmation par contrat.

Je ne me souviens pas, la prog par contrat a été abordée dans la partie fonction ? (pré et post conditions)

Après vérification, la mention était trop brève pour que le débutant puisse se raccrocher à ses connaissances ici.

alors tout peut arriver

Cette formulation me gene mais je ne sais pas exactement par quoi la remplacer.

Ce n’est peut être pas ce que vous avez cherché à faire mais on a l’impression ici que vous définissez la notion d’invariant fort qui est bien trop violente pour un débutant et pas très utile pour lui. L’invariant qui est suffisant pour avoir une programmation par contrat simple (ce qui est le cas pour le débutant), c’est l’invariant faible (à conserver comme "invariant" tout court à ce stade du tutoriel, puisque de toute façon les invariants forts ne sont nécessaires que dans le contexte de multi-threading).

L’invariant faible, son sens c’est:

  • post-condition du constructeur
  • post-condition de toute les fonctions (et donc pré-condition aussi)
  • et donc pré-condition du destructeur

En faisant un petit rappel sur les pré et post-condition, ça pourrait simplifier le discours. Un invariant c’est une condition qui est vraie avant et après chaque appel de fonction membre pour garantir sa cohérence telle que nous l’avons définie.

Mais typiquement cet invariant peut être violé au sein des fonctions membres, il doit juste être restitué avant de rendre la main à l’appelant1.

Ou bien ils peuvent être privés et seule la classe les déclarant peut les manipuler.

Ajouter une petite note pour dire qu’il existe une troisième visibilité et que ça sera vu plus tard.

La visibilité a déjà été vu avant ?

On se disait pendant le live, que ça valait peut être le coup de couper avant d’attaquer les visibilité.

Le constructeur permet d’initialiser l’objet, tout en vérifiant que les valeurs fournies sont correctes et ne risquent pas de violer les invariants.

C’est un peu imprécis. Cf plus haut. Le constructeur peut vérifier (avec un assert), la pré-condition des données d’entrées et nous garantit l’établissement de l’invariant. Avant construction, il n’y a pas de question d’invariant qui tienne : l’objet n’existe pas.


  1. enfin, il y a une subtilité: si on file this à une fonction depuis notre méthode, mais c’est un cas particulier qui pourra être abordé plus tard.

J’ai bien conscience que la redaction de la partie POO est pas simple du tout, j’espere que toutes ces remarques ne vont pas vous decourager :)

La partie procedurale du cours est au final assez lineaire en termes pedagogiques et donc il est plus simple d’organiser correctement les idees. Pour la POO, il y a pleins de notions qui arrivent en meme temps (tant pour la syntaxe que les pratiques, la methodo, etc) et c’est assez galere de conserver une progression pedagogique simple.

Pour cela que c’est pas mal d’introduire un maximum de concept dans la partie procedurale du cours (test, contrats, methodo, decoupage du code, etc) de facon a ce que ces notions soient deja connu en partie lorsque l’apprenant aborde la partie POO. Et donc soulager en partie l’apprentissage de la POO.

EDIT : j’ai pensé à un exemple concret pour illustrer cela.

La notion de visiblité private/public est une nouvelle notion de la partie POO. Pour autant, cela se base sur des concepts qui peuvent être vu avant : le découpage des fonctions entre .h et .cpp, qui est lié à la notion de d’interface public, de decoupage physique et logique du code et donc au final a des problématiques de maintenance et de qualité logicielle.

A mon sens, si l’objectif n’est pas de se focaliser uniquement sur l’apprentissage de la syntaxe (ce qui devrait etre le cas pour un cours qui vise en particulier les autodidactes), alors faudrait que l’apprenant finissent le cours en ayant assimilés qu’il y a un lien entre ces notions, une explication. (Sans bien sur aller jusqu’a la maitrise de ces notions, il faudrait un livre complet pour faire ca… et on pourrait l’appeler, au hasard, "Large Scale C++").

De plus, c’est très important en termes pédagogique d’avoir cette "distilation" progressive des notions. On retient mieux une info quand on arrive à la ratacher à des connaissances existantes. Faire le lien entre private/public vu dans la partie POO avec la notion de découpage de code, d’API public vs implémentation, etc. dans la partie procédurale peut aider a retenir et donc a comprendre.

+2 -0

Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.


Salut à tous.

Merci beaucoup @gbdivers et @Ksass`Peuk pour vos retours et votre aide. Je suis vraiment heureux de compter sur vos conseils et vos suggestions. Effectivement, écrire la partie POO est difficile, long, mais je ne me décourage pas, car ensemble on arrivera à faire un bon cours ! :)

Dans cette mise à jour, j’ai pris vos remarques en compte. Notamment la partie sur les invariants a été retravaillé. J’ai bien essayé de faire le lien avec ce que le lecteur a vu dans le chapitre sur les fonctions.

Par contre, je n’ai pas encore parlé des initialisations par défaut. Il faut que je vois dans le plan global où et comment l’introduire.

Peut etre que ca serait pertinent de parle du constructeur par defaut avant de parler des constructeurs avec paramètres.

Effectivement, je n’ai pas pensé à introduire le constructeur par défaut. Je dois réfléchir à ça.

Merci beaucoup en tout cas. :)

Par contre, je n’ai pas encore parlé des initialisations par défaut. Il faut que je vois dans le plan global où et comment l’introduire.

Ca n’a pas ete introduit dans le chapitre sur les variables ?

Effectivement, je n’ai pas pensé à introduire le constructeur par défaut. Je dois réfléchir à ça.

J’ai l’impression que vous faites la meme erreur que moi pour mon cours : rediger avant de savoir exactement ce que vous allez mettre dans les différentes parties. Cela risque d’impliquer des oublis, de la relecture et beaucoup de travail de réécriture.

Je vous conseille de peut etre faire une pause dans la rédaction et de mettre a plat le plan de cette partie. Ce que vous voulez aborder, ce qui devrait etre expliqué dans les parties avant, la progression, etc.

Je pense que je vais faire cela en live ce soir (soir pour moi), pour montrer clairement (et au propre) comment j’avais fait pour mon cours. (En plus, il faut que je le fasse pour le SG20).

+0 -0

On a pourtant fait un plan, mais je dois effectivement me replonger dedans. Les trois premiers chapitres sont déjà très avancés, mais je compte bien faire ça pour la suite. Je partagerai ici mes recherches. :)

Merci pour ton live. Je n’aurais malheureusement pas la possibilité de le regarder en direct, mais je le regarderai à tête reposée demain. D’ailleurs, ça me fait penser qu’un jour, si on a la possibilité, on devrait faire un live en direct tous les deux, histoire qu’on discute plus simplement que par message interposé. :)

On pourrait, mais ca sera un week alors, pour avoir des horaires pas trop caca pour la france.

Ce qui est important, c’est que faire des ajouts ultérieurs ne cassent pas la structure de votre cours. Sinon, c’est beaucoup de boulot.

+0 -0

Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.


Salut à tous.

Une petite mise à jour pour rajouter une partie dédiée au constructeur par défaut et à l’initialisation directe des attributs. Merci d’avance pour vos retours.

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