Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Langage C++
  3. Classes
Extrait - Langage C++ De l'héritage C au C++ moderne (avec programmes d'illustration) (2e édition)
Extraits du livre
Langage C++ De l'héritage C au C++ moderne (avec programmes d'illustration) (2e édition) Revenir à la page d'achat du livre

Classes

De la structure à la classe

En C++, une structure est une classe et elle bénéficie de toutes les fonctionnalités d’une classe. Structure et classe offrent la possibilité de regrouper en une seule unité des variables de n’importe quel type avec les fonctions associées aux traitements sur ces variables. La seule différence concerne les droits d’accès aux éléments contenus : par défaut, ils sont public pour la structure et private pour la classe (voir la section ci-après). 

Dans la structure ou la classe, les variables sont désignées par "données membres", "attributs" ou "propriétés" et les fonctions par "fonctions membres" ou "méthodes". Une variable de type structure ou classe est dénommée un "objet". Il est une instance de la classe ou de la structure dont il dépend. Il permet d’accéder aux données et fonctions membres publics qu’il regroupe. Avant d’avoir un objet, il faut d’abord définir une classe ou une structure.

Dans la suite de l’ouvrage, nous désignerons de façon générale structures et classes sous le même terme de classe et nous préciserons structure vraiment s’il arrive que ce soit indispensable.

Cet apport majeur du C++ qui réunit en une seule unité des variables pour le stockage des informations et des fonctions pour opérer des manipulations dessus détermine un changement de paradigme de programmation. En C, l’unité de traitement à la base de toutes les opérations est la fonction mais en C++, dans la logique de la programmation orienté objet, c’est la classe.

1. Des droits d’accès : public, protected, private

En C++ comme...

Accesseurs (property en anglais)

Le principe d’encapsulation des données conseille de n’avoir dans une classe que des variables private ou protected, inaccessibles des objets. Mais souvent, le problème se pose d’accéder tout de même à la valeur de la variable pour la connaître ou la modifier. Alors, puisque l’accès à une fonction membre ne contredit pas le principe d’encapsulation, l’accès à la valeur d’une variable private ou protected peut se faire via une fonction dédiée dont le nom est généralement préfixé avec get et qui retourne la valeur de la variable. De même pour modifier la valeur d’une variable private ou protected, c’est une autre fonction dont le nom est préfixé par set qui est en général proposée. Elle attend en paramètre la nouvelle valeur souhaitée.

Ce principe a été ensuite adopté nativement dans les langages C# et VB.NET sous l’appellation de « Property », qui signifie propriété en français.

1. Lire une variable private ou protected

Pour lire une variable private ou protected, la fonction retourne la valeur ou une référence constante de la variable considérée (les références sont abordées au chapitre Pointeurs). C’est une fonction très simple, en général inline, pour en augmenter la vitesse d’exécution (pas d’appel de fonction à la compilation, mais l’appel est directement remplacé par le code de la fonction). 

Soit par exemple une classe test qui contient une variable int privée, voici une fonction de récupération qui retourne la valeur de la variable :

#include <iostream> 
class test 
{ ...

Qualificatif static dans une classe

En C++, une classe ne peut pas être définie en static, seules des données ou fonctions membres de la classe le peuvent. Dans une classe, l’instruction static signifie alors une variable ou une fonction indépendante ou commune à tous les objets de la classe. À savoir que si aucun objet n’existe, ces données et fonctions sont néanmoins accessibles, et si un ou plusieurs objets existent, elles sont communes à tous. L’accès à un membre static d’une classe se fait à partir de chaque objet de la classe de façon ordinaire :

nomClasse obj1, obj2; 
    obj1.nomMembreStatic 
    obj2.nomMembreStatic 

ou sans passer par un objet avec :

nomClasse::nomMembre 

Par exemple :

#include <iostream> 
class test 
{ 
public: 
    int val; 
    static int s_val; 
    static void fonct(); 
    void affiche() { std::cout << s_val << '\n'; } 
}; 
 
// définition de la fonction static de la classe de test 
void test::fonct() 
{ 
    std::cout << "la fonction static" << '\n'; 
} 
 
/* attention : 
En C++ les variables static d'une classe doivent obligatoirement 
être initialisées en dehors de la définition de la classe sans 
répéter le mot-clé static : 
*/ 
int test::s_val = 0; 
 
int main() 
{ 
    // aucun objet   
    test::s_val = 10; 
    std::cout << test::s_val << '\n';    // affiche 10 ...

default et delete pour les fonctions spéciales de classe

1. Fonctions spéciales

Les fonctions spéciales sont les six fonctions pour lesquelles la norme C++ prévoit un comportement par défaut :

  • Constructeur par défaut

  • Constructeur de copie

  • Constructeur de déplacement

  • Affectation de copie

  • Affectation de déplacement

  • Destructeur

Destructeur, déplacement et copie sont étudiés au chapitre Pointeurs et références dans la classe.

2. Utiliser default (rappel et précision)

Soit par exemple deux classes :

#include<iostream> 
class A 
{ 
public: 
    int val; 
};  
class B 
{ 
public: 
    int val; 
    B(int val):val(val){} 
}; 

Dans un main, un objet A et un objet B :

int main() 
{ 
    A a;  // ok 
    B b;  // erreur : pas de constructeur par défaut 
  
    std::cin.get(); 
    return 0; 
} 

L’objet B pose problème parce que l’existence du constructeur ajouté par le programmeur annule le constructeur par défaut.

La première solution consiste à ajouter manuellement un constructeur par défaut :

#include<iostream> 
class B 
{ 
    int val; 
public: 
    B(){}        // constructeur sans paramètre par défaut 
    B(int val) :val(val) {} 
}; 
int main() 
{ 
    B b;        // ok 
 
    std::cin.get(); 
    return...

Surcharge des opérateurs

1. Principe

En C++, tous les opérateurs courants :

  • arithmétiques : +, -, *, /, % ; combinés : +=, -=, *=, /=, %=

  • incrémentation : ++, --

  • bit à bit : <<, >>, ~, |, & ; combinés : <<=, >>=, ~=, |=, ^=, &=

  • de comparaison : >, <, >=, <=, ==, !=

  • multicondition : &&, ||

  • pointeurs : ->, *, [ ]

  • fonction : ()

  • liste : , (la virgule)

peuvent être utilisés sur des objets. Ils sont alors considérés comme des fonctions bâties avec l’expression :

operator op 

operator est un mot-clé et op un opérateur. Par exemple : pour +, ce sera operator+ ; = ce sera operator=, etc. Une fonction operator peut être déclarée et appelée exactement de la même façon qu’une autre, l’utilisation de l’opérateur devenant un raccourci de l’appel explicite de cette fonction.

Notons par exemple les formes suivantes :

objet1 = objet2 + objet3           // si une redéfinition de + 
objet1 = operator+(objet2,objet3); // redéfinition hors classe 
objet1 = objet2.operator+(objet3); // redéfinition dans classe 

Il faut distinguer ces deux possibilités :

  • soit la redéfinition est faite hors classe, dans une fonction globale ;

  • soit la redéfinition est faite dans une classe.

Le but de l’étude que nous proposons ci-après est de montrer l’intérêt de la surcharge, mais aussi de mettre en évidence la nécessité d’être attentif à son l’écriture.

2. Fonction operator hors classe

a. Exemple addition : operator+

Soit une classe M simple, voici pour des objets...

L’instruction friend ("ami")

Une classe ou une fonction déclarée "amie" d’une classe peut accéder à toutes les données private ou protected de cette classe. Ce procédé permet de lever un interdit d’accès lorsque c’est nécessaire pour des raisons d’architecture afin d’accorder des droits entre des classes internes. Bien que ce procédé soit à éviter, il peut être manié avec précaution et nous allons le tester.

Pour déclarer dans une classe une fonction extérieure comme amie, il suffit de faire précéder la déclaration de cette fonction par l’instruction friend dans cette même classe.

class A 
{ 
    //... 
    friend void fonct()    // fonction amie de A 
    //... 
}; 

Pour déclarer une classe amie dans une autre classe, il faut déclarer cette classe précédée de l’instruction friend dans l’autre classe :

class A 
{ 
    //... 
    friend class B;  // B amie de A 
    //... 
}; 

Fonction et classe amies de la classe A ont accès aux fonctions et données private de la classe A :

#include <iostream> 
using std::cout; 
class A 
{ 
    // déclaration d'une fonction amie qui 
    // peut accéder à toutes les données 
    // private ou protected de la classe A 
    friend void fonct(); 
 
    // déclaration d'une classe amie qui peut 
    // accéder à...