La programmation en C++ moderne

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

a marqué ce sujet comme résolu.

Le code n’est valable qu’a partir de C++ 17. Le compilateur gcc 9.3 ne compile par en C++17 par défaut à priori.

(/home/J0039644/src/zeste)===> gdb -quiet ./a
Reading symbols from ./a...done.
(gdb) br main
Breakpoint 1 at 0x1004010a5: file main.cpp, line 20.
(gdb) run
Starting program: /home/J0039644/src/zeste/a
[New Thread 6268.0x461c]
[New Thread 6268.0x432c]
[New Thread 6268.0x51e8]
[New Thread 6268.0x5f78]
[New Thread 6268.0x1cc4]

Thread 1 "a" hit Breakpoint 1, main () at main.cpp:20
20              Toto t = f();
(gdb) info source
Current source file is main.cpp
Compilation directory is /home/J0039644/src/zeste
Located in /home/J0039644/src/zeste/main.cpp
Contains 21 lines.
Source language is c++.
Producer is GNU C++14 9.3.0 -mtune=generic -march=x86-64 -g.
Compiled with DWARF 2 debugging format.
Does not include preprocessor macro info.
+0 -0

Salut à tous.

Un gros morceau aujourd’hui puisqu’on vous livre le chapitre sur la sémantique d’entité. Et y’a de quoi faire, même s’il reste des notions que je veux aborder.

  • La portée protected ?
  • Comment appeler une fonction d’une classe mère, pour réutiliser des comportements déjà codés.
  • Les exercices, pour bien faire pratiquer ce chapitre difficile.

Merci à tous pour votre aide et vos retours. Surtout qu’on vous livre des gros pavés. Merci, vos efforts sont ce qui ont fait de ce cours ce qu’il est aujourd’hui ! ;)

@informaticienzero


Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.

Si ça peut donner des idées…

La portée protected ?

Je l’aborde en live en reprenant mon exemple de chat intoxiqué, et au lieu que le protagoniste soit le voisin à qui je donne les clés de la baraque, cela devient les enfants qui ne vont plus forcément respecter les invariants de porte qui doit rester fermée.

Sinon, ma conclusion haut niveau est généralement: données privés, mais services techniques protégés qui respectent les invariants.

Comment appeler une fonction d’une classe mère, pour réutiliser des comportements déjà codés.

Je ne l’aborde jamais sans faire une parenthèse avec le DP template method. Car seul ce dernier permet de contrôler exactement où est le point de variation. Avec l’appel de la fonction parente, on ne peut rajouter que du code avant ou après. Et in-fine, la vielle règle MISRA qui dit qu’il ne faut pas redéfinir une fonction qui a du code est loin d’être idiote.

Sinon, je n’ai pas trop le temps dans l’immédiat j’y jetterai un coup d’oeil plus tard.

Salut à tous.

Au programme, on a retravaillé les chapitres sur la sémantique d’entité et sur les handles. Nous sommes donc ouverts à toutes vos remarques et suggestions. Merci d’avance à tous. :)

@gbdivers : tu peux relire les deux chapitres pré-cités, ainsi que celui sur la sémantique de mouvement. Je n’ai pas de diff à montrer parce que déjà cette fonctionnalité manque sur ZdS, puis aussi parce que y’a eu des grosses réécritures sur ces trois chapitres. Merci pour ton aide, je sais qu’on te file un gros morceau là. ^^

PS : je suis tombé sur un cours POO aujourd’hui qui n’a pas l’air comme les autres. J’ai fais une lecture rapide, mais on parle de contrats, d’invariants, de SOLID. :)


Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.

Bonjour,

Une petite typo: dans le chapitre https://zestedesavoir.com/contenus/beta/822/la-programmation-en-c-moderne/la-programmation-orientee-objet/la-semantique-de-mouvement/#2-rvalues-et-rvalue-references-1, vous dite:

Les rvalues, acronymes de … comme le temporaire m1 + m2 + m3 qu’on a vu en exemple dans la section précédente.

Hors, dans l’exemple précédent, c’est m3 = m1 + m2, donc le temporaire devait être m1 + m2.

Cordialement.

Bonjour,

Sauf erreur de ma part, il y a une petite erreur dans le dernier exemple du chapitre https://zestedesavoir.com/contenus/beta/822/la-programmation-en-c-moderne/la-programmation-orientee-objet/classes-a-semantique-dentites/#exemple-concret. votre exemple doit afficher:

Des décisions importantes doivent être prises aujourd'hui pour le service DSI !

hors, le code est:

void Directeur::assister_reunions() const noexcept
{
    std::cout << "Des décisions importantes doivent être prises aujourd'hui !\n";
}

le code devait être:

void Directeur::assister_reunions() const noexcept
{
    std::cout << "Des décisions importantes doivent être prises aujourd'hui pour le service " << m_direction << " !\n";
}

De plus, je vous pose la question de la meilleur syntaxe (des () ou des {}) pour:

Directeur::Directeur(std::string const& nom, std::string const& prenom, int salaire, std::string const& direction)
    : Personne(nom, prenom, salaire), m_direction(direction)

ou peut être

Directeur::Directeur(std::string const& nom, std::string const& prenom, int salaire, std::string const& direction)
    : Personne{nom, prenom, salaire}, m_direction{direction}

Autre remarque, dans le premier exemple du chapitre https://zestedesavoir.com/contenus/beta/822/la-programmation-en-c-moderne/la-programmation-orientee-objet/classes-a-semantique-dentites/#le-principe-de-substitution-de-liskov, ligne 19, c’est la première fois que l’on trouve la directive "protected:", et elle n’est pas présentée.

Cordialement.

+3 -0

Salut à tous.

Pas mal de modification sur les entités et sur l’héritage au programme de cette bêta. On a décidé de parler de NVI et pour cela, on a rajouté un chapitre sur l’héritage, sinon le chapitre entités aurait été bien trop lourd (en plus de faire un peu du hors-sujet).

On ne sait pas encore si on va parler de protected, j’y réfléchis. En attendant, n’hésitez pas à relire ces deux chapitres et à nous dire s’ils vous paraissent clairs, s’il manque des choses, si vous voyez d’autres notions importantes à rajouter.

Merci d’avance à tous. :)


Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.

Bonjour,

Oups! J’ai beau lire, relire ces chapitres, c’est pas encore clair pour moi … Mais ça, c’est mon problème.

Et comme je les lis et relis, je remarque des détailles ("c’est pas bien grave, mon bon m’sieur")

Dans le chapitre https://zestedesavoir.com/contenus/beta/822/la-programmation-en-c-moderne/la-programmation-orientee-objet/classes-a-semantique-dentites/#h%C3%A9ritage-et-constructeurs, dans le 2ème exemple de code, vous avez inclus "#include <cassert>", alors qu’il n’y a pas de "assert" dans le code. Pour le premier exemple, le code proposé n’est pas complet. Si j’étais vous, j’ajouterai au debut de l’exemple 3 points:

...
int main()
{
    Directeur const patron { "Ron"s, "Pat"s, 2500 };
    patron.travailler();

    return 0;
}

Je ne comprend pas l’objectif du chapitre https://zestedesavoir.com/contenus/beta/822/la-programmation-en-c-moderne/la-programmation-orientee-objet/classes-a-semantique-dentites/#une-question-de-visibilit%C3%A9. Il n’est pas fini ? Vous allez y introduire la description de la directive "protected:" ?

M’enfin ! Si ça peut aider …

Cordialement

Merci pour tes retours. :)

vous avez inclus "#include <cassert>", alors qu’il n’y a pas de "assert" dans le code

Oui bien vu. Je le mets à chaque fois dans mes tests, mais je ne pense pas toujours à le retirer lors de l’écriture du tutoriel.

Pour le premier exemple, le code proposé n’est pas complet. Si j’étais vous, j’ajouterai au debut de l’exemple 3 points:

Bien vu, on va faire ça.

Je ne comprend pas l’objectif du chapitre https://zestedesavoir.com/contenus/beta/822/la-programmation-en-c-moderne/la-programmation-orientee-objet/classes-a-semantique-dentites/#une-question-de-visibilit%C3%A9. Il n’est pas fini ? Vous allez y introduire la description de la directive "protected:" ?

Le but est d’expliquer à un débutant que tout ce qui est private n’est pas accessible dans les classes enfants. Ça peut sembler évident, mais c’est toujours mieux quand on le dit. :)

Pour protected, je ne sais pas encore. Il présente des risques décapsulatoires (pour reprendre les propos de @lmghs) plus élevés et si tu utilises le NVI, les situations où la fonction membre virtuelle doit être protected et non private sont rares. J’y réfléchis toujours.

Bonjour,

je sais avec mes remarques de détail, à 3 sous, de 4 fois rien, … (J’ai pas trouvé avec 5 !), vous allez trouver que j’exagère, … Mais promis, … c’est pour la bonne cause !

Dans le chapitre https://zestedesavoir.com/contenus/beta/822/la-programmation-en-c-moderne/la-programmation-orientee-objet/la-semantique-de-mouvement/#2-rvalues-et-rvalue-references-1, j’ai compilé l’exemple de LMGHS. J’avais activé le plus de détection de warning dans ma version de projet. Et comme j’en avais un peut trop (de warnings), j’ai un peu modifier l’exemple. De plus, je me suis posé la question d’un liréral (pas long temps, j’avais déjà la réponse …). Enfin, voila ce que ça donne.

#include <iostream>
#include <string>

using namespace std::string_literals;

std::string source() noexcept
{
    return "src"s;
}

std::string f(std::string const & s) noexcept
{
    std::cout << "f(&)\n"s;
    return s;
}

std::string f(std::string const && s) noexcept
{
    std::cout << "f(&&)\n"s;
    return s;
}

int main()
{
    // un litéral est une rvalue
    std::cout << "f(\"str\"s) -> ";
    f("str"s);

    // Ici, varloc est une lvalue.
    std::string varloc { "vl" };
    std::cout << "f(varloc) -> ";
    f(varloc);

    // Au contraire, le résultat de source() est une rvalue.
    std::cout << "f(source()) -> ";
    f(source());

    return 0;
}

Si ça peut être utile.

Cordialement.

Disons que mon exemple présente l’avantage de pouvoir très facilement de commenter ou décommenter une définition tout en gardant un code lisible et concis afin d’explorer les définitions incompatibles entre elles. Tout décommenté le code n’est pas sensé compiler, c’est fait pour.

Qu’avais-tu comme warnings une fois les set compatibles identifiés?

BTW, ceci n’a pas vraiment d’utilité: std::string const && s

.
.
.
Mais. Ce n’est pas ça mon exemple. Mon exemple est le suivant:

#include <string>
#include <iostream>

std::string source() { return "src"; }

void f(std::string const& s) { std::cout << "f(const&)\n"; }
void f(std::string &      s) { std::cout << "f(&)\n";      }
void f(std::string &&     s) { std::cout << "f(&&)\n";     }
void f(std::string        s) { std::cout << "f()\n";       }

#define call(x)  std::cout << #x << "\t-> "; f(x);

int main ()
{
    std::string varloc = "vl";
    call(varloc);

    std::string const constvarloc = "cvl";
    call(constvarloc);

    call(source());
}

Attention avec les littéraux, seuls les numériques sont des rvalues, les littéraux de chaîne sont des lvalue. Mais "str"s est une std::string qui est bien une rvalue.

Personnellement, « sémantique de mouvement » m’a toujours dérangé, je lui préfère « sémantique de déplacement » qui reflète mieux le fait de prendre quelque chose pour le mettre ailleurs.

Luc,

je viens de compiler ton dernier code, j’ai les warning et erreur suivants:

g++ -c -pipe -pedantic-errors -pedantic -Wextra -Wall -fexceptions -Wnon-virtual-dtor -Wshadow -Winit-self -Wredundant-decls -Wcast-align -Wundef -Wfloat-equal -Winline -Wunreachable-code -Wmissing-declarations -Wmissing-include-dirs -Wswitch-enum -Wswitch-default -Weffc++ -Wzero-as-null-pointer-constant -Wmain -g -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_CORE_LIB -I../toBeDeleted -I. -isystem /usr/include/x86_64-linux-gnu/qt5 -isystem /usr/include/x86_64-linux-gnu/qt5/QtCore -I. -I/usr/lib/x86_64-linux-gnu/qt5/mkspecs/linux-g++ -o main.o ../toBeDeleted/main.cpp
../toBeDeleted/main.cpp:4:13: warning: no previous declaration forstd::string source()’ [-Wmissing-declarations]
    4 | std::string source() { return "src"; }
      |             ^~~~~~
../toBeDeleted/main.cpp:6:6: warning: no previous declaration forvoid f(const string&)’ [-Wmissing-declarations]
    6 | void f(std::string const& s) { std::cout << "f(const&)\n"; }
      |      ^
../toBeDeleted/main.cpp: In function ‘void f(const string&)’:
../toBeDeleted/main.cpp:6:27: warning: unused parameter ‘s’ [-Wunused-parameter]
    6 | void f(std::string const& s) { std::cout << "f(const&)\n"; }
      |        ~~~~~~~~~~~~~~~~~~~^
../toBeDeleted/main.cpp: At global scope:
../toBeDeleted/main.cpp:7:6: warning: no previous declaration forvoid f(std::string&)’ [-Wmissing-declarations]
    7 | void f(std::string &      s) { std::cout << "f(&)\n";      }
      |      ^
../toBeDeleted/main.cpp: In function ‘void f(std::string&)’:
../toBeDeleted/main.cpp:7:27: warning: unused parameter ‘s’ [-Wunused-parameter]
    7 | void f(std::string &      s) { std::cout << "f(&)\n";      }
      |        ~~~~~~~~~~~~~~~~~~~^
../toBeDeleted/main.cpp: At global scope:
../toBeDeleted/main.cpp:8:6: warning: no previous declaration forvoid f(std::string&&)’ [-Wmissing-declarations]
    8 | void f(std::string &&     s) { std::cout << "f(&&)\n";     }
      |      ^
../toBeDeleted/main.cpp: In function ‘void f(std::string&&)’:
../toBeDeleted/main.cpp:8:27: warning: unused parameter ‘s’ [-Wunused-parameter]
    8 | void f(std::string &&     s) { std::cout << "f(&&)\n";     }
      |        ~~~~~~~~~~~~~~~~~~~^
../toBeDeleted/main.cpp: At global scope:
../toBeDeleted/main.cpp:9:6: warning: no previous declaration forvoid f(std::string)’ [-Wmissing-declarations]
    9 | void f(std::string        s) { std::cout << "f()\n";       }
      |      ^
../toBeDeleted/main.cpp: In function ‘void f(std::string)’:
../toBeDeleted/main.cpp:9:27: warning: unused parameter ‘s’ [-Wunused-parameter]
    9 | void f(std::string        s) { std::cout << "f()\n";       }
      |        ~~~~~~~~~~~~~~~~~~~^
../toBeDeleted/main.cpp: In function ‘int main()’:
../toBeDeleted/main.cpp:11:49: error: call of overloaded ‘f(std::string&)’ is ambiguous
   11 | #define call(x)  std::cout << #x << "\t-> "; f(x);
      |                                                 ^
../toBeDeleted/main.cpp:16:5: note: in expansion of macro ‘call’
   16 |     call(varloc);
      |     ^~~~
../toBeDeleted/main.cpp:6:6: note: candidate: ‘void f(const string&)’
    6 | void f(std::string const& s) { std::cout << "f(const&)\n"; }
      |      ^
../toBeDeleted/main.cpp:7:6: note: candidate: ‘void f(std::string&)’
    7 | void f(std::string &      s) { std::cout << "f(&)\n";      }
      |      ^
../toBeDeleted/main.cpp:9:6: note: candidate: ‘void f(std::string)’
    9 | void f(std::string        s) { std::cout << "f()\n";       }
      |      ^
../toBeDeleted/main.cpp:11:49: error: call of overloaded ‘f(const string&)’ is ambiguous
   11 | #define call(x)  std::cout << #x << "\t-> "; f(x);
      |                                                 ^
../toBeDeleted/main.cpp:19:5: note: in expansion of macro ‘call’
   19 |     call(constvarloc);
      |     ^~~~
../toBeDeleted/main.cpp:6:6: note: candidate: ‘void f(const string&)’
    6 | void f(std::string const& s) { std::cout << "f(const&)\n"; }
      |      ^
../toBeDeleted/main.cpp:9:6: note: candidate: ‘void f(std::string)’
    9 | void f(std::string        s) { std::cout << "f()\n";       }
      |      ^
../toBeDeleted/main.cpp:11:49: error: call of overloaded ‘f(std::string)’ is ambiguous
   11 | #define call(x)  std::cout << #x << "\t-> "; f(x);
      |                                                 ^
../toBeDeleted/main.cpp:21:5: note: in expansion of macro ‘call’
   21 |     call(source());
      |     ^~~~
../toBeDeleted/main.cpp:6:6: note: candidate: ‘void f(const string&)’
    6 | void f(std::string const& s) { std::cout << "f(const&)\n"; }
      |      ^
../toBeDeleted/main.cpp:8:6: note: candidate: ‘void f(std::string&&)’
    8 | void f(std::string &&     s) { std::cout << "f(&&)\n";     }
      |      ^
../toBeDeleted/main.cpp:9:6: note: candidate: ‘void f(std::string)’
    9 | void f(std::string        s) { std::cout << "f()\n";       }
      |      ^

les [-Wmissing-declarations], c’est normal, c’est pas un warning très utile, mais les [-Wunused-parameter] ça me semble plus grave, aussi, je me suis permis de changer :

void f(....) { ....; }

en

std::string f(....) { ....; return s; }

Cordialement.

PS: Cependant, et c’est normal, le nouvel exemple ne compile que quand on met en commentaire la ligne :

void f(std::string        s) { std::cout << "f()\n";       }
+0 -0

OK. Effectivement,

  • [-Wmissing-declarations] n’a AMA aucun intérêt
  • [-Wunused-parameter], c’est vrai que je n’utilise pas le paramètre. Il peut être retiré. Ce n’est pas bien important dans le contexte présent. Rien de critique. C’est moins grave AMA qu’une fonction qui renvoie quelque chose que l’on utilise pas, et ne rien faire du paramètre est important pour montrer que
std::string varloc = "toto";
call(move(varloc));
std::cout << "Alors? Volé ou pas volé?: " << varloc.length() << "\n";

Rajouter un une_globale = move(s); dans f(string &&) à ce moment change la donne.

Bref, aucun de ces warning n’a d’importance ici. Si vraiment ça distrait, je dirai donc au choix:

void f(std::string) {...}
// ou
void f(std::string s) { std::ignore = s; ... }
// ou
void f(std::string s) { (void)s; ... }

Cependant, et c’est normal, le nouvel exemple ne compile que quand on met en commentaire la ligne 

Il y a la combinaison opposée qui compile. L’exemple peut servir à (dé?)montrer que :

  • le passage par valeur est incompatible avec toutes les autres surcharges avec référence: il y aura obligatoirement une ambiguité.
  • que référence n’est pas compatible avec rvalue; et qui sont les rvalues
  • que rev-ref n’est pas compatible avec lvalue
  • la référence constante est la moins prioritaire

Attention avec les littéraux, seuls les numériques sont des rvalues, les littéraux de chaîne sont des lvalue. Mais "str"s est une std::string qui est bien une rvalue.

Personnellement, « sémantique de mouvement » m’a toujours dérangé, je lui préfère « sémantique de déplacement » qui reflète mieux le fait de prendre quelque chose pour le mettre ailleurs.

jo_link_noir

Quand on voit la page de la doc https://en.cppreference.com/w/cpp/language/value_category, c’est un sujet très (trop) complexe de savoir exactement ce qui est une rvalue ou autre.

Du coup, si un cours nécessite que le débutant sache distinguer les rvalues, les lvalues, etc. pour comprendre et écrire du code correct, ca serait un très gros problème pédagogique.

(Dit autrement, osef de ces categories)

Pour l’usage de "sémantique de déplacement", on avait deja eu la discussion ici il me semble ? Je préfère aussi "déplacement" (et de mémoire, c’est ce que j’utilisais dans mon cours).

+0 -0

Salut à tous.

Au programme, on a un nouveau chapitre qui va parler de NVI et des limites de l’héritage. S’il manque des choses, ou que des explications méritent d’être étoffées / complétées, n’hésitez pas à faire signe.

Du coup, on compte envoyer les sept premiers chapitres en validation, c’est-à-dire depuis le premier jusqu’à celui sur NVI. Pour le reste, notamment les handles, il y a un peu de réécriture à faire, donc ça viendra plus tard.

@Dedeun : tu ne nous embête pas, au contraire, tes remarques sont toujours utiles et appréciées. :)

Merci d’avance à tous.

@informaticienzero


Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.

Voila je voulais signaler une toute petite erreur que j avais remarque en lisant ce cours vraiment génial et qui de plus est gratuit […]

II. On passe la deuxième

  1. Découpons du code - Les fonctions

Dessine moi une fonction

Code du cours :

bool est_pair(int i)
{
    if (i == 0)
    {
        return true;
    }
    else if (i < 0)
    {
        return est_pair(-i);
    }
    else
    {
        return est_impair(i - 1);
    }        
}

bool est_impair(int i)
{
    if (i < 0)
    {
        return est_impair(-i);
    }
    else
    {
        return est_pair(i - 1);
    }
}

Je pense qu’il y a un cas ou on ne sort pas des appels de fonctions : Quand i = 1 et qu’on fait un appel de la fonction est_pair () en premier comme ceci :

#include <iostream>

bool est_pair (int i);
bool est_impair (int i);

int main ()
{
    int const i { 1 };
    bool pair { est_pair (i) };

    std::cout << i << " : " << pair << std::endl;

    return 0;
}

bool est_pair(int i)
{
    if (i == 0)
    {
        return true;
    }
    else if (i < 0)
    {
        return est_pair(-i);
    }
    else
    {
        return est_impair(i - 1);
    }        
}

bool est_impair(int i)
{
    if (i < 0)
    {
        return est_impair(-i);
    }
    else
    {
        return est_pair(i - 1);
    }
}

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

Segmentation fault (core dumped)

En faisant ré-écrivant la fonction est_impair () comme ci-dessous :

bool est_impair(int i)
{
    if (i == 0)
    {
        return est_pair (i);
    }

    else if (i < 0)
    {
        return est_impair(-i);
    }

    else
    {
        return est_pair(i - 1);
    }
}

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 […]

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.

+0 -0
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