La programmation en C++ moderne

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

a marqué ce sujet comme résolu.

Salut à tous,

Je reviens avec une mise à jour qui consiste à enlever la partie sur les invariants et l’encapsulation du premier chapitre, pour le mettre dans le deuxième. Ça permet de faire sur un seul chapitre les invariants, ce que c’est, les constructeurs pour les définir, explicit, etc. Le deuxième est toujours en rédaction, mais il va reprendre quasiment tout ce qu’il y avait dans l’existant.


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.

Petite mise à jour avec complétion de la partie sur les constructeurs. Il reste maintenant la partie explicit et les exercices à rédiger.

Merci d’avance à tous pour votre aide.


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.

Au programme, la section sur explicit, plus des petites corrections diverses. Globalement, les deux premiers chapitres sont terminés (sauf les parties exercices, que je dois étoffer). Je vais maintenant m’attaquer au chapitre 3, sur la sémantique de valeur, en détaillant mieux les opérateurs, leurs formes canoniques, etc.

Merci d’avance pour vos retours.


Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.

Hello.

C’est cool ce que je vois. Mes petits pinaillages.


La solution pour respecter nos invariants s’appelle l’encapsulation. Plutôt que de laisser nos attributs librement modifiables par le premier venu, nous allons les protéger, les masquer

Je ne suis pas d’accord avec le terme "masquer", surtout aussi vite. Je préfère p.ex: "les enrober d’une couche protectrice (ou pourquoi on parle d’en- capsul -ation). Techniquement, en C++ et les langages qui s’en sont inspiré, on fait ça en cachant/masquant les informations/données qui participent à l’invariant, voire toutes les données.". (vu que Python ou Eiffel ont suivi un autre chemin)


Fraction pow(Fraction const & fraction, int puissance)

Cette fonction devrait être statique. Vu que c’est un peu tôt pour en parler, je la supprimerai. Perso, j’aurai juste fourni Fraction Fraction::pow(int) const.

Cet exemple montre juste la construction par copie. Puisqu’on en parle au chapitre suivant…


Pas d’exception car on ne fait qu’initialiser deux entiers.

A ce compte là, on peut tout mettre en noexcept. Mais n’est-ce pas trop tôt comme sujet?


définir comme explicit tout constructeur prenant un argument

Je formulerai plutôt la règle générale avec

définir comme explicit tout constructeur pouvant prendre un seul argument — au sens pouvant être appelé avec un seul paramètre réel


Un exemple que j’utilise en général pour explicit, c’est les tableaux

void f(std::vector<double>);
...
f(42);

"Imaginez ce qu’il se passerait si le constructeur mono-argument entier des vecteurs étaient un constructeur de conversion, i.e. non explicite".

Au delà de la lisibilité, on encourt des risques de bugs. Cf les tristement célèbres

struct S {
    S(std::string);
    S(double);
};
bool operator==(S, S);
...

if ("toto"s == 42.5)

Merci pour tes retours. :)

Fraction pow(Fraction const & fraction, int puissance)

Cette fonction devrait être statique. Vu que c’est un peu tôt pour en parler, je la supprimerai. Perso, j’aurai juste fourni Fraction Fraction::pow(int) const.

Cet exemple montre juste la construction par copie. Puisqu’on en parle au chapitre suivant…

lmghs

En fait, quelles sont les réflexions à mener sur le choix « fonction libre » vs « fonction statique ». On est d’accord que les deux font aussi partie des services de ma classe, non ? Dans quel cas privilégier telle ou telle forme ?

Pas d’exception car on ne fait qu’initialiser deux entiers.

A ce compte là, on peut tout mettre en noexcept. Mais n’est-ce pas trop tôt comme sujet?

lmghs

Je ne sais pas. Ils ont déjà vu noexcept au moment où ils ont appris à lire la documentation. Je comptais aussi introduire la possibilité d’ajouter noexcept et constexpr dans le chapitre I de la partie POO, juste après la partie sur const. Sauf s’il s’avère que c’est trop compliqué pour le lecteur. À voir.

C’est une question de simplicité d’utilisation pour moi. Ce qui me parait naturel est

constexpr auto zero = Fraction::zero();
constexpr auto one  = Fraction::one();

auto const x = Fraction{42, 9};

auto const x2 = pow(x,2);
// ou éventuellement (mais bof)
auto const x2 = x.pow(2);

// ----------
// avoir
x.pow(2);
// qui change x est très bizarre pour moi car Fraction a une sémantique de valeur
// Et donc toute fonction qui traite les fractions comme des entités qui mutent
// ça introduit de la surprise en ce qui me concerne. Les opérateurs composés 
// c'est une chose (`+=`, `/=`, etc), mais une fonction mathématique qui altère 
// ses paramètres, c'est non conventionnel. 
// C'est comme avoir un `list::operator+` qui mute une liste.
// `list::append()`, c'est parfait: 'append' porte le sens de modifier.
// mais +, certes pas.
// C'est pareil pour pow AMA

// ----------
// faire
auto x3 = Fraction::pow(x, 3);
// me parait très maladroit en C++ où l'ADL/Koenig-lookup sont nos "amis" (!= Java)

namespace mymaths {
    class Fraction { ... }
    Fraction pow(Fraction, int n);
    // A vérifier si déclarer ami améliore résolution de noms sans introduire de bruit
    // --> je ne sais plus
}
....

template <typename N>
auto somefunc(N v) {
    using std::pow;

    // Ca ne marchera pas avec Fraction::pow statique
    auto const res = pow(v, 5) + f<N>();
    return res;
}

Je préfère en priorité la version libre non statique, et à défaut la version membre.

C’est, ici, pour une question d’uniformité des interfaces. Après, cela n’empêche pas d’écrire une version libre à partir d’une version membre. Cf swap sur les conteneurs standards. Pour une fraction ça me parait inutile.

Saut à tous.

Je me posais la question sur le fait de parler de constexpr dans le premier chapitre. Autant pour noexcept je vois bien l’intérêt et je trouve que ça a bien sa place. Autant pour constexpr, je me demande s’il ne vaudrait pas mieux attendre le chapitre sur les classes templates. Je trouve que ça y a plus sa place que dès le premier chapitre.

Qu’en pensez-vous ?

Pourquoi pas dans le chapitre sur les fonctions ? Voir dans le chapitre sur les variables (pour déclarer des constantes).

Je ne suis pas sûr de voir en quoi il serait plus pertinent d’en parler que dans le chapitre sur les classes templates ?

+0 -0

En fait, à ce stade, le lecteur connait déjà constexpr. Je voulais parler de comment le réintroduire au moment de la POO ? Dès le premier chapitre avec noexcept ? Ou bien plutôt au moment des classes templates, ce qui permet de parler à un seul endroit de l’exception « Oui je sais, les implémentations doivent normalement aller dans des .cpp mais là on va les mettre dans le .hpp ».

Je parlerai de constexpr dans le chapitre des variables pour déclarer les constantes, mais je retarderai la présentation des fonctions constexpr car c’est un sujet avancé qui frôle le sujet de l’inlining. On peut faire un teasing (ou un index), mais je n’en parlerai pas au début.

Dans mes formations, je le range dans les formations "expert" et "HPC & optims". Je n’en parle pas dans les formations "débutant" et "initié/révision" (faussement appelée "avancée")

Salut à tous.

Au programme, j’ai rajouté une partie sur l’amitié au moment de la distinction libre / membre. J’ai aussi commencé à retravailler le chapitre sur la sémantique de valeur. Il me reste la partie sur les opérateurs à écrire et on sera bon, modulo vos retours. :)


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.

Au programme, les trois premiers chapitres sont prêts. Il ne nous reste plus qu’à écrire les tests et les corrections pour les exercices du chapitre sur la sémantique de valeur, ainsi qu’à écrire les tests pour le TP et on sera prêt pour envoyer en validation. :D


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, cours très bien fait, bravo.

Qq petites coquilles:

Dans le chapitre sur les flux, au début de la présentation de ofstream:

Cela nous est utilise si nous …

Dans le chapitre "Une classe de grande valeur", "Si les maths et vous" (en bas de la page):

pas très à l’aide en mathématiques …

Dans le chapitre sur Les classes à sémantique d’entité, Retour à la classe Personne, ligne 11

virual au lieu de virtual

Voila

+1 -0

Salut à tous.

Merci à toi @bluescaster, je file corriger ça.

Au programme de cette mise à jour, des captures d’écran à jour pour Visual Studio 2019, les quatre premiers chapitres POO fin prêts et une demande de validation ! On attend avec impatience de pouvoir proposer une première partie sur la POO à tous les lecteurs, partie qui est très demandée. :)


Bonjour les agrumes !

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

Merci d’avance pour vos commentaires.

Une petite question: Autant cela parait logique d’interdire la copie d’une classe à sémantique d’entité autant je ne comprend pas pourquoi interdire systématiquement le déplacement ?

Par exemple je me suis amusé à écrire une classe Socket que j’utilise ensuite dans une classe qui sert le serveur HTTP. Pour moi une socket est qq chose d’unique sur une machine, comme un fichier, donc sémantique d’entité. Pourtant j’ai besoin d’avoir la liste de tout les clients connectés à mon serveur, que je gère grâce à un vecteur de Socket. Si je supprime le déplacement d’une socket, je suis coincé, et allouer sur le tas des Sockets composées d’un int et d’un bool pour les gérer avec des handles c’est pas très efficace non ?

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