Fondamentaux C++ (Suite)

Niveau: Débutant Durée: 8 heures Programmation

Suite du cours sur les fondamentaux de C++. Cette partie couvre les tableaux et chaînes, les pointeurs et références, les classes et objets, ainsi que des exercices pratiques.

8. Tableaux et Chaînes (Suite)

Tableaux (Suite)

Voici quelques opérations courantes sur les tableaux:

Déclaration et initialisation:

// Déclaration
int nombres[5];  // Tableau de 5 entiers

// Initialisation
int scores[3] = {75, 83, 90};

// Initialisation avec taille implicite
char voyelles[] = {'a', 'e', 'i', 'o', 'u'};

Accès aux éléments:

// Accès par indice (commence à 0)
int premier = scores[0];  // 75
scores[1] = 85;           // Modification
Attention: C++ ne vérifie pas les limites des tableaux. Accéder à un indice hors limites peut causer des comportements indéfinis!

Chaînes de caractères

Il existe deux façons principales de gérer les chaînes en C++:

Style C (tableaux de caractères):

// Chaîne terminée par le caractère nul '\0'
char nom[10] = "Alice";

// Équivalent à:
char nom2[10] = {'A', 'l', 'i', 'c', 'e', '\0'};

// Fonctions de manipulation (nécessite #include <cstring>)
char dest[20];
strcpy(dest, nom);      // Copie
strcat(dest, " Smith"); // Concaténation
int longueur = strlen(nom); // Longueur

Classe std::string (moderne):

#include <string>

// Déclaration et initialisation
std::string prenom = "Alice";
std::string nom = "Smith";

// Concaténation
std::string nom_complet = prenom + " " + nom;

// Méthodes utiles
int longueur = nom_complet.length();
std::string sous_chaine = nom_complet.substr(0, 5);  // "Alice"
nom_complet.replace(0, 5, "Bob");  // "Bob Smith"
Conseil: Préférez std::string aux chaînes style C quand c'est possible. Elles sont plus sûres et plus faciles à utiliser.

9. Pointeurs et Références

Pointeurs

Les pointeurs sont des variables qui stockent des adresses mémoire:

Déclaration et initialisation:

// Déclaration d'un pointeur vers un int
int* ptr;

// Obtention de l'adresse d'une variable
int nombre = 42;
ptr = &nombre;  // & est l'opérateur d'adresse

// Initialisation directe
int* ptr2 = &nombre;

Déréférencement:

// Accès à la valeur pointée avec *
int valeur = *ptr;  // valeur = 42

// Modification de la valeur pointée
*ptr = 100;  // nombre vaut maintenant 100

Pointeur nul:

// Pointeur qui ne pointe vers rien
int* ptr_nul = nullptr;  // C++11 et plus

// Vérification avant utilisation
if (ptr_nul != nullptr) {
    *ptr_nul = 10;  // Sécurisé
}
Attention: Déréférencer un pointeur nul ou non initialisé cause un comportement indéfini, souvent un plantage du programme!

Arithmétique des pointeurs

Les opérations sur les pointeurs:

int tableau[5] = {10, 20, 30, 40, 50};
int* ptr = tableau;  // Pointe vers le premier élément

// Accès aux éléments
int premier = *ptr;        // 10
int deuxieme = *(ptr + 1); // 20

// Incrémentation
ptr++;                     // Pointe maintenant vers tableau[1]
int val = *ptr;            // 20

// Relation avec les indices
int val2 = *(tableau + 2); // Équivalent à tableau[2]
Note: L'arithmétique des pointeurs tient compte de la taille du type. Pour un pointeur int*, ptr + 1 avance de 4 octets (taille d'un int).

Références

Les références sont des alias pour des variables existantes:

Déclaration et initialisation:

int nombre = 42;
int& ref = nombre;  // ref est une référence à nombre

Utilisation:

// Modification via la référence
ref = 100;  // nombre vaut maintenant 100

// Pas besoin de déréférencement
int valeur = ref;  // valeur = 100

Références comme paramètres:

// Passage par référence
void incrementer(int& x) {
    x++;  // Modifie la variable originale
}

int n = 5;
incrementer(n);  // n vaut maintenant 6
Avantages des références:
  • Syntaxe plus propre que les pointeurs (pas de déréférencement)
  • Ne peuvent pas être nulles
  • Doivent être initialisées à la déclaration
  • Ne peuvent pas être réassignées à une autre variable

10. Classes et Objets

Définition de classe

Les classes sont les briques de base de la programmation orientée objet en C++:

class Rectangle {
private:
    // Attributs (membres de données)
    double largeur;
    double hauteur;
    
public:
    // Constructeur
    Rectangle(double l, double h) {
        largeur = l;
        hauteur = h;
    }
    
    // Méthodes (fonctions membres)
    double calculerAire() {
        return largeur * hauteur;
    }
    
    double calculerPerimetre() {
        return 2 * (largeur + hauteur);
    }
    
    // Accesseurs (getters)
    double getLargeur() {
        return largeur;
    }
    
    // Mutateurs (setters)
    void setLargeur(double l) {
        if (l > 0) largeur = l;
    }
};

Utilisation:

// Création d'un objet
Rectangle rect(5.0, 3.0);

// Appel de méthodes
double aire = rect.calculerAire();       // 15.0
double perimetre = rect.calculerPerimetre(); // 16.0

// Utilisation des accesseurs/mutateurs
double l = rect.getLargeur();  // 5.0
rect.setLargeur(6.0);          // Modification

Constructeurs et destructeurs

Fonctions spéciales pour l'initialisation et le nettoyage:

class Personne {
private:
    std::string nom;
    int age;
    
public:
    // Constructeur par défaut
    Personne() {
        nom = "Inconnu";
        age = 0;
    }
    
    // Constructeur avec paramètres
    Personne(std::string n, int a) {
        nom = n;
        age = a;
    }
    
    // Constructeur de copie
    Personne(const Personne& autre) {
        nom = autre.nom;
        age = autre.age;
    }
    
    // Destructeur
    ~Personne() {
        // Nettoyage des ressources si nécessaire
        std::cout << "Destruction de " << nom << std::endl;
    }
};

Liste d'initialisation:

// Syntaxe alternative pour les constructeurs
Personne(std::string n, int a) : nom(n), age(a) {
    // Corps du constructeur (peut être vide)
}
Note: La liste d'initialisation est plus efficace, surtout pour les membres constants et les objets.

Héritage et polymorphisme

L'héritage permet de créer des hiérarchies de classes:

// Classe de base
class Forme {
protected:
    std::string couleur;
    
public:
    Forme(std::string c) : couleur(c) {}
    
    // Méthode virtuelle
    virtual double calculerAire() {
        return 0.0;
    }
    
    // Destructeur virtuel (important!)
    virtual ~Forme() {}
};

// Classe dérivée
class Cercle : public Forme {
private:
    double rayon;
    
public:
    Cercle(std::string c, double r) : Forme(c), rayon(r) {}
    
    // Redéfinition de la méthode
    double calculerAire() override {
        return 3.14159 * rayon * rayon;
    }
};

Polymorphisme:

// Utilisation polymorphique
Forme* forme1 = new Cercle("Rouge", 5.0);
double aire = forme1->calculerAire();  // Appelle Cercle::calculerAire()

// Nettoyage
delete forme1;  // Appelle le destructeur de Cercle
Important: Toujours déclarer le destructeur comme virtuel dans les classes de base pour assurer un nettoyage correct des objets dérivés.

11. Exercices Pratiques

Exercice 1: Calculatrice simple

Créez une calculatrice qui effectue les opérations de base (+, -, *, /).

Consignes:

  1. Créez une classe Calculatrice avec des méthodes pour chaque opération
  2. Implémentez un menu utilisateur dans la fonction main()
  3. Gérez les erreurs (division par zéro)

Solution partielle:

#include <iostream>

class Calculatrice {
public:
    double additionner(double a, double b) {
        return a + b;
    }
    
    // Implémentez les autres méthodes...
};

int main() {
    Calculatrice calc;
    char operation;
    double a, b;
    
    std::cout << "Entrez une opération (+, -, *, /): ";
    std::cin >> operation;
    
    std::cout << "Entrez deux nombres: ";
    std::cin >> a >> b;
    
    // Implémentez le reste...
    
    return 0;
}

Exercice 2: Gestionnaire de contacts

Créez un système simple de gestion de contacts.

Consignes:

  1. Créez une classe Contact avec des attributs comme nom, téléphone, email
  2. Créez une classe GestionnaireContacts qui stocke et gère une liste de contacts
  3. Implémentez des fonctionnalités pour ajouter, supprimer, rechercher et afficher des contacts
  4. Utilisez des vecteurs (std::vector) pour stocker les contacts

Structure suggérée:

#include <iostream>
#include <string>
#include <vector>

class Contact {
private:
    std::string nom;
    std::string telephone;
    std::string email;
    
public:
    // Constructeur, getters, setters...
};

class GestionnaireContacts {
private:
    std::vector<Contact> contacts;
    
public:
    void ajouterContact(const Contact& c) {
        contacts.push_back(c);
    }
    
    // Autres méthodes...
};

int main() {
    // Implémentez un menu utilisateur
    return 0;
}
Conseil: Cet exercice vous permet de pratiquer plusieurs concepts: classes, vecteurs, entrées/sorties, et gestion de la mémoire.