Banque de cours - Cours 5 - Tableaux (Arrays), Vecteurs (Vectors) et Chaînes de caractèr
23 min de lecture
# Cours 5 - Tableaux (Arrays), Vecteurs (Vectors) et Chaînes de caractères (Strings)
Où est Charlie le Bug ?
Sujets couverts : Variables, boucles, fonctions, passage de paramètres
void doubleValue(int x) { x = x * 2;}int add(int a, int b);int add(int a, int b) { int sum = a + b;}int calculate(int x = 5, int y) { }void process(bool condition) { int result; if (condition) { result = 10; } cout << result;}
Solution
void doubleValue(int x) { // Bug 1: should be reference (int& x) to modify original x = x * 2;}int add(int a, int b); // Declarationint add(int a, int b) { // Bug 2: missing return statement! int sum = a + b;}int calculate(int x = 5, int y) { } // Bug 3: default params must go right-to-leftvoid process(bool condition) { int result; if (condition) { result = 10; } cout << result; // Bug 4: result may be uninitialized if condition is false}
Tableaux fixes : std::array
Tableaux de style C (les connaître, les éviter)
int scores[5] {90, 85, 78, 92, 88};// Problems:// - Don't know their own size// - Decay to pointers when passed to functions// - No bounds checking// - Can't be returned from functions
std::array<T, N> : Le tableau moderne de taille fixe
#include <array>std::array<int, 5> scores {90, 85, 78, 92, 88};scores.size(); // Returns 5scores[0]; // Access first element (no bounds check)scores.at(0); // Access with bounds check (throws if out of range)
Tutoriel : Boucle for avec un tableau
// Using a for loop with an array#include <iostream>#include <format>#include <array>int main(){ std::array<unsigned, 6> height {26, 37, 47, 55, 62, 75}; // An array of heights unsigned total {}; for (std::size_t i {} ;i < height.size(); ++i) { total += height[i]; } const auto average{ static_cast<double>(total) / height.size() }; std::cout << std::format("The average height is {:.1f}.", average) << std::endl; unsigned count {}; for (std::size_t i {}; i < height.size(); ++i) { if (height[i] < average) ++count; } std::cout << std::format("{} people are below average height.", count) << std::endl;}
Concepts clésstd::array connaît sa propre taille — plus besoin d'une variable size séparée
static_cast<double>(total) empêche la division entière
{:.1f} formate un nombre à virgule flottante avec 1 décimale
mais justement, sa taille est fixe!
Obtenir la taille d'un tableau
// Obtaining the number of array elements#include <iostream>#include <format>#include <array>int main(){ std::array values {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; // CTAD: std::array<int, 10> std::cout << std::format("There are {} elements in the array.", values.size()) << std::endl; int sum {}; for (std::size_t i {}; i < values.size(); ++i) { sum += values[i]; } std::cout << std::format("The sum of the array elements is {}.", sum) << std::endl;}
Boucle for basée sur les intervalles (aperçu)
std::array values {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};for (int val : values) // Range-based for - iterates every element{ std::cout << std::format("{} ", val);}
Accéder à un élément (avec vérification des bornes)
front()
Premier élément
back()
Dernier élément
insert()
Insérer à l’index spécifié
std::string : Pas seulement des tableaux de char
// Concatenating strings#include <iostream>#include <format>#include <string>int main(){ std::string first; // Stores the first name std::string second; // Stores the second name std::cout << std::format("Enter your first name: "); std::cin >> first; // Read first name std::cout << std::format("Enter your last name: "); std::cin >> second; // Read second name std::string sentence {"Your full name is "}; // Create basic sentence sentence += first + " " + second + "."; // Augment with names std::cout << std::format("{}", sentence) << std::endl; std::cout << std::format("The string contains {} characters.", sentence.length()) << std::endl;}
Tutoriel : Compter les voyelles et les consonnes
// Accessing characters in a string#include <iostream>#include <format>#include <string>#include <cctype>int main(){ std::string text; std::cout << std::format("Enter a line of text:") << std::endl; std::getline(std::cin, text); // Read a line including spaces unsigned vowels {}; unsigned consonants {}; for (std::size_t i {}; i < text.length(); ++i) { if (std::isalpha(text[i])) // Check for a letter { switch (std::tolower(text[i])) // Convert to lowercase { case 'a': case 'e': case 'i': case 'o': case 'u': case 'y': ++vowels; break; default: ++consonants; break; } } } std::cout << std::format("Your input contained {} vowels and {} consonants.", vowels, consonants) << std::endl;}
std::cin >> texte
L'opérateur d'extraction >> lit les caractères depuis l'entrée standard jusqu'au premier espace blanc (espace, tabulation ou retour à la ligne). Les espaces blancs en début de saisie sont ignorés.
std::string texte;std::cin >> texte;// Saisie : "Bonjour le monde"// Résultat : texte == "Bonjour" ← le reste est perdu
std::getline(std::cin, texte)std::getline() lit toute la ligne jusqu'au caractère de retour à la ligne '\n'. Les espaces sont conservés. Le '\n' est consommé mais n'est pas stocké dans la chaîne.
std::string texte;std::getline(std::cin, texte);// Saisie : "Bonjour le monde"// Résultat : texte == "Bonjour le monde" ✓
Tableau comparatif
Comportement
std::cin >> texte
std::getline()
S’arrête à l’espace
✓
✗
Conserve les espaces
✗
✓
Ignore les espaces en début
✓
✗
Consomme le '\n' final
✗
✓
Le piège classique : mélanger les deux
Lorsqu'on utilise std::cin >>avantstd::getline(), un '\n' résiduel reste dans le buffer d'entrée (input buffer). std::getline() le lit immédiatement et retourne une chaîne vide.
int age;std::string nom;std::cout << "Entrez votre âge : ";std::cin >> age;// L'utilisateur tape "25" puis appuie sur Entrée.// Le '25' est lu, mais le '\n' reste dans le buffer.std::cout << "Entrez votre nom complet : ";std::getline(std::cin, nom);// std::getline() lit immédiatement le '\n' résiduel.// Résultat : nom == "" ← chaîne vide, la saisie est ignorée !
Pourquoi ça arrive ?
Buffer après std::cin >> age :[ '\n' ] ^ std::getline() s'arrête ici immédiatement
La solution : vider le buffer
Après un std::cin >>, il faut consommer le '\n' résiduel avant d'appeler std::getline().
Option 1 — std::cin.ignore() (simple)
std::cin >> age;std::cin.ignore(); // consomme un seul caractère (le '\n')std::getline(std::cin, nom);
#include <limits>std::cin >> age;std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');// Consomme tout ce qui reste dans le buffer jusqu'au '\n' inclusstd::getline(std::cin, nom);
Cette forme est préférable si l’utilisateur a pu saisir des caractères supplémentaires après la valeur (ex. : "25 abc\n").
Dès que std::cin >> et std::getline() coexistent dans un programme, il faut toujours vider le buffer entre les deux. C’est l’une des erreurs les plus fréquentes chez les débutants en C++.
☝🏼
Conseil: toujours utiliser std::getline(std::cin, my_string) plutôt que std::cin >> my_string lorsqu’il s’agit de lire une ligne de la console
ColorLab v2 - Palettes, Couleurs Web & Opérations sur les Chaînes
Rappel : où en sommes-nous ?
Dans le ColorLab v1 (semaine 4), on a introduit struct Color pour regrouper le nom et les composantes RGB, et on a vu comment les fonctions manipulent ces structs avec le passage par référence :
Color soleil {"Rouge Soleil", 220, 60, 10};Color ocean {"Bleu Océan", 10, 80, 210};Color fusion = mixColors(soleil, ocean); // 2 paramètres au lieu de 9 !displayColor(fusion); // 1 paramètre au lieu de 4 !
C’est bien plus propre qu’avec des int séparés. Mais on travaille toujours avec des couleurs individuelles. Et si on voulait gérer une palette de 12 couleurs web ? Les chercher par nom ? Les trier ? Il nous faut des collections.
L'évolution : des couleurs individuelles aux collections
On a déjà notre struct Color de la semaine 4. Ce qui change cette semaine, c'est qu'on peut maintenant les organiser en collections et exploiter les opérations sur les chaînes :
**std::vector<Color>** — une palette dynamique qui peut grandir à volonté
**std::string** — on exploite find(), npos et le formatage hex pour chercher et convertir les noms de couleurs
// colors.h - v2// ==============// Déclarations pour le ColorLab v2 : palettes, couleurs web, opérations sur les chaînes.#ifndef COLORS_H#define COLORS_H#include <string>#include <vector>// --- struct Color (défini en semaine 4 — rappel) ---struct Color { std::string name; // Nom de la couleur int r {}; // Rouge (0-255) int g {}; // Vert (0-255) int b {}; // Bleu (0-255)};// Garde une valeur dans la plage [0, 255].// Paramètre BY REFERENCE - modifie l'original.void clamp(int& value);// Crée une couleur avec validation et clamping automatique.// Retourne un Color par valeur.Color createColor(const std::string& name, int r, int g, int b);// Affiche une couleur avec un bloc ANSI coloré dans le terminal.// Paramètre BY CONST REFERENCE - lecture seule, efficace.void displayColor(const Color& color);// Calcule la luminosité moyenne pondérée d'une couleur (0–255).// Paramètre BY CONST REFERENCE.int brightness(const Color& color);// Mélange deux couleurs et retourne le résultat.// Les deux couleurs d'entrée sont BY CONST REFERENCE - on ne les modifie pas.Color mixColors(const Color& a, const Color& b, const std::string& mixName);// --- NOUVEAU v2 : fonctions qui utilisent std::vector et std::string ---// Affiche toutes les couleurs d'une palette.// Le vector est passé BY CONST REFERENCE - on ne modifie pas la palette.void displayPalette(const std::vector<Color>& palette);// Cherche une couleur par nom exact dans la palette.// Retourne l'index si trouvée, -1 sinon.int findByName(const std::vector<Color>& palette, const std::string& name);// Cherche toutes les couleurs dont le nom contient une sous-chaîne.// Utilise std::string::find() — retourne un vector d'indices.std::vector<int> findByPartialName(const std::vector<Color>& palette, const std::string& partial);// Convertit une couleur en chaîne hexadécimale CSS : "#RRGGBB".// Utilise std::format pour le formatage hexadécimal.std::string colorToHex(const Color& color);// Retourne une palette de couleurs web standard prédéfinies.std::vector<Color> getWebColors();#endif // COLORS_H
Ce qui a changé par rapport à v1
Aspect
v1 (semaine 4)
v2 (semaine 5)
struct Color
Introduit (name, r, g, b)
Réutilisé tel quel
Collections
Couleurs individuelles
std::vector<Color> — palettes dynamiques
Couleurs prédéfinies
Aucune
12 couleurs web (getWebColors())
Recherche
Non disponible
findByName(), findByPartialName()
Opérations sur chaînes
std::string pour les noms
string::find(), formatage hex
Format de sortie
std::cout << chaîné
std::format partout
Itération
Pas de collection à itérer
Range-based for sur la palette
2 - Le Source File (colors.cpp) - v2
// colors.cpp - v2// ================// Définitions pour le ColorLab v2 : palettes, recherche, hex.#include "colors.h"#include <iostream>#include <format>// ------------------------------------------------------------// clamp - inchangé depuis v1// ------------------------------------------------------------void clamp(int& value) { if (value < 0) value = 0; if (value > 255) value = 255;}// ------------------------------------------------------------// createColor - factory function avec clamping automatique// ------------------------------------------------------------Color createColor(const std::string& name, int r, int g, int b) { clamp(r); clamp(g); clamp(b); return Color{name, r, g, b};}// ------------------------------------------------------------// displayColor - affiche un bloc coloré ANSI// BY CONST REFERENCE - on ne modifie pas la couleur// ------------------------------------------------------------void displayColor(const Color& color) { std::cout << std::format(" [{:<20s}] ", color.name); std::cout << "\033[38;2;" << color.r << ";" << color.g << ";" << color.b << "m"; std::cout << "████████"; std::cout << "\033[0m"; std::cout << std::format(" R:{:<3d} G:{:<3d} B:{:<3d}", color.r, color.g, color.b); std::cout << std::endl;}// ------------------------------------------------------------// brightness - luminosité moyenne pondérée // The standard formula is a **weighted average**, most commonly the ITU-R BT.709 luma coefficient used in modern displays://// Y = 0.2126·R + 0.7152·G + 0.0722·B// The weights reflect the fact that the human eye is **far more sensitive to green** than to red, and barely sensitive to blue. This is due to the distribution of cone cells in the retina — the M (medium) cones responsible for green perception are the most numerous.// BY CONST REFERENCE - lecture seule// ------------------------------------------------------------int brightness(const Color& color) { return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;}// ------------------------------------------------------------// mixColors - prend 2 Color, retourne un Color// BY CONST REFERENCE pour les entrées// ------------------------------------------------------------Color mixColors(const Color& a, const Color& b, const std::string& mixName) { return Color{mixName, (a.r + b.r) / 2, (a.g + b.g) / 2, (a.b + b.b) / 2};}// ============================================================// NOUVEAU v2 : fonctions qui exploitent std::vector et std::string// ============================================================// ------------------------------------------------------------// displayPalette - affiche toutes les couleurs d'un vector// Le vector est passé BY CONST REFERENCE - efficace et sûr// On utilise un range-based for pour itérer// ------------------------------------------------------------void displayPalette(const std::vector<Color>& palette) { std::cout << std::format("=== Palette ({} couleurs) ===", palette.size()) << std::endl; for (const Color& c : palette) { displayColor(c); }}// ------------------------------------------------------------// findByName - recherche linéaire par nom exact// Retourne l'index ou -1 si non trouvée// ------------------------------------------------------------int findByName(const std::vector<Color>& palette, const std::string& name) { for (std::size_t i {0}; i < palette.size(); ++i) { if (palette[i].name == name) { return static_cast<int>(i); } } return -1;}// ------------------------------------------------------------// findByPartialName - recherche par sous-chaîne// Utilise string::find() pour chercher un fragment dans chaque nom.// Si find() retourne std::string::npos, le fragment n'a pas été trouvé.// Retourne un vector d'indices — peut être vide si rien ne correspond.// ------------------------------------------------------------std::vector<int> findByPartialName(const std::vector<Color>& palette, const std::string& partial) { std::vector<int> results; for (std::size_t i {0}; i < palette.size(); ++i) { if (palette[i].name.find(partial) != std::string::npos) { results.push_back(static_cast<int>(i)); } } return results;}// ------------------------------------------------------------// colorToHex - convertit une couleur en string hexadécimale CSS// Utilise std::format avec {:02X} pour formater chaque composante// en 2 chiffres hexadécimaux majuscules (00-FF).// Retourne une std::string par valeur.// ------------------------------------------------------------std::string colorToHex(const Color& color) { return std::format("#{:02X}{:02X}{:02X}", color.r, color.g, color.b);}// ------------------------------------------------------------// getWebColors - retourne une palette de couleurs web connues// Les noms viennent du standard CSS/SVG - vos étudiants les// connaissent peut-être déjà du web !// ------------------------------------------------------------std::vector<Color> getWebColors() { return { {"Teal", 0, 128, 128}, {"Salmon", 250, 128, 114}, {"Coral", 255, 127, 80}, {"Goldenrod", 218, 165, 32}, {"Orchid", 218, 112, 214}, {"SteelBlue", 70, 130, 180}, {"Tomato", 255, 99, 71}, {"MediumAquamarine", 102, 205, 170}, {"SlateGray", 112, 128, 144}, {"Crimson", 220, 20, 60}, {"LimeGreen", 50, 205, 50}, {"DarkOrange", 255, 140, 0} };}
Points importants à noter
**std::vector<Color>** peut contenir autant de couleurs qu’on veut, et on peut en ajouter dynamiquement avec push_back().
**Range-based ****for** : for (const Color& c : palette) est bien plus lisible que for (int i = 0; i < palette.size(); ++i).
**string::find()** retourne la position de la sous-chaîne trouvée, ou std::string::npos si elle n’existe pas. C’est le patron classique pour chercher dans une chaîne.
**std::format("{:02X}", value)** formate un entier en hexadécimal (X) sur au moins 2 caractères (02), avec des zéros à gauche. Parfait pour les codes couleur CSS.
**findByPartialName()** retourne un std::vector<int> — un vector qu’on construit dans la fonction avec push_back() et qu’on retourne par valeur. C’est un bon exemple d’utilisation dynamique d’un vector.
3 - main.cpp - v2
// main.cpp - ColorLab v2// =======================// Palettes, couleurs web, recherche par nom et codes hex.#include "colors.h"#include <iostream>#include <format>#ifdef _WIN32#include <windows.h>#endif#ifdef _WIN32void enableAnsiColors() { // Set console to UTF-8 SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); // Enable ANSI escape sequences HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwMode = 0; GetConsoleMode(hOut, &dwMode); SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);}#endifint main() { #ifdef _WIN32 enableAnsiColors(); #endif std::cout << "=== ColorLab v2 - Palettes & Couleurs Web ===" << "\n\n"; // --- Créer des couleurs avec notre struct (rappel semaine 4) --- Color soleil = createColor("Rouge Soleil", 220, 60, 10); Color ocean = createColor("Bleu Océan", 10, 80, 210); std::cout << "--- Nos couleurs personnalisées ---" << std::endl; displayColor(soleil); displayColor(ocean); std::cout << std::endl; // --- Mélanger deux couleurs --- Color fusion = mixColors(soleil, ocean, "Fusion"); std::cout << "--- Mélange ---" << std::endl; displayColor(fusion); std::cout << std::format(" Luminosité de {} : {}/255", fusion.name, brightness(fusion)) << "\n\n"; // --- Charger la palette de couleurs web --- std::vector<Color> palette = getWebColors(); displayPalette(palette); std::cout << std::endl; // --- Conversion en hexadécimal (std::string + std::format) --- std::cout << "--- Codes hexadécimaux ---" << std::endl; for (const Color& c : palette) { std::cout << std::format(" {:<20s} → {}", c.name, colorToHex(c)) << "\n"; } std::cout << std::endl; // --- Rechercher une couleur par nom exact --- std::string recherche {"Coral"}; int index = findByName(palette, recherche); if (index >= 0) { std::cout << std::format("'{}' trouvé à l'index {} :", recherche, index) << "\n"; displayColor(palette[index]); std::cout << std::format(" Code hex : {}", colorToHex(palette[index])) << "\n"; } else { std::cout << std::format("'{}' non trouvé dans la palette.", recherche) << "\n"; } std::cout << std::endl; // --- Recherche partielle (string::find + vector retourné) --- std::cout << "--- Recherche partielle : couleurs contenant 'Dark' ou 'Green' ---" << std::endl; std::vector<int> darkColors = findByPartialName(palette, "Dark"); std::vector<int> greenColors = findByPartialName(palette, "Green"); std::cout << std::format(" '{}' : {} résultat(s)", "Dark", darkColors.size()) << "\n"; for (int idx : darkColors) { displayColor(palette[idx]); } std::cout << std::format(" '{}' : {} résultat(s)", "Green", greenColors.size()) << "\n"; for (int idx : greenColors) { displayColor(palette[idx]); } std::cout << std::endl; // --- Ajouter nos couleurs personnalisées à la palette --- palette.push_back(soleil); palette.push_back(ocean); palette.push_back(fusion); std::cout << std::format("Palette mise à jour : {} couleurs au total.", palette.size()) << "\n\n"; // --- Afficher les luminosités de toute la palette --- std::cout << "=== Luminosités ===" << std::endl; for (const Color& c : palette) { std::cout << std::format(" {:<20s} : {}/255", c.name, brightness(c)) << "\n"; } std::cout << "\n=== Séance de Labo Terminée ===" << std::endl; return 0;}
Compiler et exécuter
Compilez dans CLion (Build), l’icône Marteau
Exécutez dans un cmd.exe
5 - Sortie attendue
☝🏼
CLion sur Windows malheureusement a un terminal qui ne supporte pas bien les couleurs. Pour bien voir le résultat, exécutez colorlab.exe, qui se trouve dans votre dosser cmake-build.debug, dans un command line (cmd.exe).
☝🏼
Remarque sur Windows, avez-vous remarqué qu’il y a un espace de trop dans l’espace réservé au nom “Rouge Soleil”?
Le piège UTF-8 et std::format en C++
Le problème
Quand on utilise std::format avec un padding fixe sur des chaînes contenant des caractères accentués (é, è, ê, etc.), l’alignement se décale sur Windows.
{:<20s} remplit jusqu’à 20 octets, pas 20 caractères visuels. En UTF-8, un caractère accentué comme é occupe 2 octets au lieu de 1. Résultat : std::format croit que la chaîne est plus longue qu’elle ne l’est visuellement, et ajoute moins d’espaces.
Exemple concret
Nom
Caractères visuels
Octets UTF-8
Décalage
Rouge Soleil
12
12
aucun
Bleu Océan
10
11 (é = 2 octets)
1 espace en moins
Sur Mac, le terminal compense souvent ce décalage. Sur Windows, non.
La solution
On compte les octets « en trop » dus aux caractères multi-octets, puis on augmente la largeur de padding en conséquence.
void displayColor(const Color& color) { // Compter la longueur visuelle (en caractères, pas en octets) int visualLen = 0; for (std::size_t i = 0; i < color.name.size(); ) { unsigned char ch = color.name[i]; if (ch < 0x80) { i += 1; } // ASCII : 1 octet else if (ch < 0xE0) { i += 2; } // 2 octets (accents latins) else if (ch < 0xF0) { i += 3; } // 3 octets (CJK, symboles) else { i += 4; } // 4 octets (emojis) visualLen++; } // Ajuster le padding : on ajoute la différence octets - caractères int padding = 20 + (static_cast<int>(color.name.size()) - visualLen); std::cout << std::format(" [{:<{}s}] ", color.name, padding); // ... reste de l'affichage}
Pourquoi ça marche
Pour "Bleu Océan" :
color.name.size() → 11 (octets)
visualLen → 10 (caractères)
padding → 20 + (11 - 10) = 21
std::format remplit jusqu’à 21 octets, ce qui donne visuellement 20 colonnes — alignement parfait.
Rappel : encodage UTF-8
Plage
Octets
Exemples
U+0000 – U+007F
1
A, z, 0, !
U+0080 – U+07FF
2
é, è, ñ, ü
U+0800 – U+FFFF
3
中, █, →
U+10000 – U+10FFFF
4
😀, 🎨
En résumé:
std::format avec {:<20s} remplit jusqu’à ce que la chaîne fasse 20 octets. Comme "Bleu Océan" fait déjà 11 octets (à cause du é), std::format n’ajoute que 9 espaces au lieu de 10. Visuellement, la chaîne ne fait que 10 caractères + 9 espaces = 19 colonnes au lieu de 20.
Donc "Bleu Océan" prend plus de place en mémoire (11 octets), mais moins de place à l’écran (19 colonnes) — c’est justement le piège. Le terminal affiche par colonnes visuelles, std::format compte en octets. Le é « mange » un espace de padding.
Récapitulatif : qu'avons-nous appris ?
Concept
Où on l’a vu
**std::vector<T>**
std::vector<Color> palette — palette dynamique
**push_back()**
Ajouter des couleurs à la palette
**.size()**
Nombre de couleurs dans la palette
**Range-based ****for**
for (const Color& c : palette)
**Retour d’un ****vector**
findByPartialName() construit et retourne un std::vector<int>
**string::find()**
Chercher une sous-chaîne dans le nom d’une couleur
**std::string::npos**
Valeur retournée par find() quand la sous-chaîne n’existe pas
**std::format("{:02X}")**
Formatage hexadécimal pour colorToHex()
Recherche linéaire
findByName() — parcourir un vector pour trouver un élément
Recherche par sous-chaîne
findByPartialName() — combiner string::find() et vector
Erreurs courantes à surveiller
1. Accéder à un index invalide dans un vector
std::vector<Color> palette = getWebColors();displayColor(palette[99]); // DANGER - dépasse la taille ! Comportement indéfini.// Utilisez .at(99) pour obtenir une exception au lieu d'un crash silencieux
2. Oublier **&** dans le range-based for
for (Color c : palette) { // COPIE chaque couleur à chaque itération !for (const Color& c : palette) { // Référence - pas de copie (préféré)
3. Comparaison de strings sensible à la casse
if (palette[i].name == "Coral") // OK - comparaison exacteif (palette[i].name == "coral") // FAUX - C++ est sensible à la casse !// Même chose pour find() : "dark" ne trouvera pas "DarkOrange"
**4. Oublier de vérifier **npos** après ****find()**
// FAUX - find() retourne std::string::npos (pas -1 !) si non trouvéif (palette[i].name.find("Dark") >= 0) { ... }// CORRECT - comparer avec nposif (palette[i].name.find("Dark") != std::string::npos) { ... }
5. Utiliser un vector retourné sans vérifier s’il est vide
std::vector<int> results = findByPartialName(palette, "Purple");displayColor(palette[results[0]]); // DANGER si results est vide !// CORRECT - vérifier d'abordif (!results.empty()) { displayColor(palette[results[0]]);}
Travail personnel
Lecture
Lire Beginning C++23 Chapitres 6-7
Défi 1 - Tri par luminosité
Ajoutez une fonction sortByBrightness qui trie une palette du plus sombre au plus lumineux. Vous pouvez utiliser un algorithme de tri simple (selection sort) - on verra std::sort dans les semaines suivantes.
// Déclaration dans colors.h :void sortByBrightness(std::vector<Color>& palette);
Affichez la palette avant et après le tri pour vérifier le résultat.
Défi 2 - Couleur la plus proche
Écrivez une fonction qui, étant donné des valeurs R, G, B quelconques, trouve la couleur la plus proche dans la palette (par distance de Manhattan) :
// Déclaration dans colors.h :int findClosest(const std::vector<Color>& palette, int r, int g, int b);
La « distance » entre deux couleurs se calcule comme : distance = |r1-r2| + |g1-g2| + |b1-b2|
Défi 3 - Gradient entre deux couleurs de la palette (avancé)
Ajoutez une fonction displayGradient qui affiche une transition progressive entre deux couleurs dans le terminal. Combinez-la avec findByName() pour chercher les couleurs dans la palette :
// Déclaration dans colors.h :void displayGradient(const Color& from, const Color& to, int steps);
Testez en affichant un gradient entre “Crimson” et “LimeGreen” de votre palette web. Utilisez std::lerp de <cmath> (C++20) pour interpoler chaque composante.
Solutions des défis
⚠️ Essayez d’abord par vous-même avant de regarder !
// ------------------------------------------------------------// sortByBrightness - selection sort par luminosité// La palette est passée BY REFERENCE - on la modifie directement// ------------------------------------------------------------void sortByBrightness(std::vector<Color>& palette) { for (std::size_t i {0}; i < palette.size(); ++i) { std::size_t minIdx {i}; for (std::size_t j {i + 1}; j < palette.size(); ++j) { if (brightness(palette[j]) < brightness(palette[minIdx])) { minIdx = j; } } if (minIdx != i) { Color temp = palette[i]; palette[i] = palette[minIdx]; palette[minIdx] = temp; } }}
Utilisation dans **main.cpp** :
std::cout << "=== Avant le tri ===" << std::endl;displayPalette(palette);sortByBrightness(palette);std::cout << "\\n=== Après tri par luminosité ===" << std::endl;displayPalette(palette);
Solution - Défi 2
Déclaration dans **colors.h** :
int findClosest(const std::vector<Color>& palette, int r, int g, int b);
Définition dans **colors.cpp** :
#include <cstdlib> // pour std::abs// ------------------------------------------------------------// findClosest - trouve la couleur la plus proche par distance de Manhattan// La palette est BY CONST REFERENCE - on ne la modifie pas// ------------------------------------------------------------int findClosest(const std::vector<Color>& palette, int r, int g, int b) { int bestIndex {-1}; int bestDistance {999999}; for (std::size_t i {0}; i < palette.size(); ++i) { int distance = std::abs(palette[i].r - r) + std::abs(palette[i].g - g) + std::abs(palette[i].b - b); if (distance < bestDistance) { bestDistance = distance; bestIndex = static_cast<int>(i); } } return bestIndex;}
Utilisation dans **main.cpp** :
std::cout << "--- Couleur la plus proche de (200, 50, 50) ---" << std::endl;int closest = findClosest(palette, 200, 50, 50);if (closest >= 0) { std::cout << std::format(" C'est '{}' !", palette[closest].name) << std::endl; displayColor(palette[closest]);}
Solution - Défi 3
Déclaration dans **colors.h** :
void displayGradient(const Color& from, const Color& to, int steps);
Définition dans **colors.cpp** :
#include <cmath> // pour std::lerp (C++20)// ------------------------------------------------------------// displayGradient - affiche une ligne de blocs colorés// interpolés entre deux couleurs// ------------------------------------------------------------void displayGradient(const Color& from, const Color& to, int steps) { std::cout << std::format(" [{}] → [{}]", from.name, to.name) << std::endl; std::cout << " "; for (int i {0}; i < steps; ++i) { float t = static_cast<float>(i) / (steps - 1); int r = static_cast<int>(std::lerp(static_cast<float>(from.r), static_cast<float>(to.r), t)); int g = static_cast<int>(std::lerp(static_cast<float>(from.g), static_cast<float>(to.g), t)); int b = static_cast<int>(std::lerp(static_cast<float>(from.b), static_cast<float>(to.b), t)); std::cout << "\\033[38;2;" << r << ";" << g << ";" << b << "m" << "██"; } std::cout << "\\033[0m" << std::endl;}
Point clé : Remarquez comment findByName() et displayGradient() travaillent ensemble : on cherche par nom dans la palette, puis on passe les couleurs trouvées au gradient. C’est la puissance des collections + fonctions combinées.