- Listes d'initialisation
1.1 Affectation dans le corps du constructeur
Lors de la création d'un objet, le compilateur appelle un constructeur pour fournir des valeurs initiales aux membres de l'objet.
class Date {
public:
Date(int an, int mois, int jour) {
_an = an;
_mois = mois;
_jour = jour;
}
private:
int _an;
int _mois;
int _jour;
};
Bien que les membres reçoivent des valeurs, ce procédé dans le corps du constructeur est une affectation, pas une initialisation. L'initialisation ne peut se produire qu'une seule fois, tandis que l'affectation dans le corps peut être répétée.
1.2 Syntaxe de la liste d'initialisation
La liste d'initialisation commence par deux-points, suivie d'une liste de membres séparés par des virgules, chacun étant associé à une valeur entre parenthèses.
class Date {
public:
Date(int an, int mois, int jour)
: _an(an), _mois(mois), _jour(jour) {}
private:
int _an;
int _mois;
int _jour;
};
1.3 Membres nécessitant une liste d'initialisation
Certains types de membres doivent être initialisés via une liste d'initialisation :
- Membres références
- Membres constants (
const) - Membres de types définis par l'utilisateur sans constructeur par défaut
class Conteneur {
public:
Conteneur(int val, int& ref, const int cte)
: _objet(val), _reference(ref), _constante(cte) {}
private:
TypePersonnalise _objet; // Suppose que TypePersonnalise n'a pas de constructeur par défaut.
int& _reference;
const int _constante;
};
1.4 Ordre d'initialisation
Les membres sont initialisés selon leur ordre de déclaration dans la classe, et non selon leur ordre dans la liste d'initialisation.
class Illustration {
public:
Illustration(int val)
: _second(val), _premier(_second) {}
void Afficher() { /* ... */ }
private:
int _second; // Déclaré en premier, initialisé en premier.
int _premier; // Déclaré en second, initialisé avec la valeur actuelle de _second.
};
1.5 Valeurs par défaut à la déclaration (C++11)
Les membres peuvent recevoir une valeur par défaut lors de leur déclaration. Cette valeur est utilisée si le membre n'est pas explicitement initialisé dans la liste d'initialisation.
class Configuration {
public:
Configuration() : _param2(10) {}
private:
int _param1 = 5; // Valeur par défaut utilisée.
int _param2; // Initialisé explicitement à 10.
int _param3; // Non initialisé explicitement, pas de valeur par défaut (comportement indéfini pour int).
};
1.6 Le mot-clé explicit
Les constructeurs peuvent permettre des conversions implicites. Le mot-clé explicit interdit ce comportement pour les constructeurs à un seul paramètre ou avec des paramètres par défaut.
class Metre {
public:
explicit Metre(double valeur) : _valeur(valeur) {}
double ObtenirValeur() const { return _valeur; }
private:
double _valeur;
};
void Fonction(Metre m) { /* ... */ }
int main() {
Metre m1(5.0); // OK.
// Metre m2 = 5.0; // Erreur de compilation si explicit.
// Fonction(5.0); // Erreur de compilation si explicit.
return 0;
}
- Membres statiques
2.1 Concept
Les membres statiques (static) appartiennent à la classe elle-même plutôt qu'à une instance particulière. Une variable statique est partagée par tous les objets de la classe et doit être définie et initialisée à l'extérieur de la classe.
class Compteur {
public:
Compteur() { ++_nombre; }
~Compteur() { --_nombre; }
static int ObtenirNombre() { return _nombre; }
private:
static int _nombre; // Déclaration.
};
int Compteur::_nombre = 0; // Définition et initialisation.
2.2 Caractéristiques
- Les membres statiques sont partagés par tous les objets.
- Les variables statiques sont définies hors de la classe (sans le mot-clé
static). - Les membres statiques peuvent être accessibles via
Classe::membreouobjet.membre. - Les fonctions statiques n'ont pas de pointeur
thisimplicite et ne peuvent accéder aux membres non statiques qu'indirectement.
- Fonctions et classes amies
3.1 Fonctions amies
Les fonctions amies (friend) peuvent accéder aux membres privés et protégés d'une classe. Elles sont déclarées à l'intérieur de la classe avec le mot-clé friend mais définies à l'extérieur.
class Vecteur {
friend std::ostream& operator<<(std::ostream& flux, const Vecteur& v);
public:
Vecteur(double x, double y) : _x(x), _y(y) {}
private:
double _x, _y;
};
std::ostream& operator<<(std::ostream& flux, const Vecteur& v) {
flux << "(" << v._x << ", " << v._y << ")";
return flux;
}
3.2 Classes amies et classes internes
Une classe peut être déclarée amie d'une autre classe, lui donnant accès à tous ses membres. Une classe interne est une classe définie à l'intérieur d'une autre. La classe interne est implicitement amie de la classe englobante, mais l'inverse n'est pas vrai.
class Externe {
private:
int _donnee;
public:
class Interne { // Classe interne.
public:
void Acceder(const Externe& e) {
// Peut accéder à e._donnee car Interne est amie de Externe.
}
};
};
- Objets anonmyes
Un objet anonyme est créé en appelant directement le constructeur sans nom de variable. Sa durée de vie est limitée à l'expression courante.
class Temporaire {
public:
Temporaire(int v) : _val(v) { /* ... */ }
int Obtenir() const { return _val; }
private:
int _val;
};
int Calculer(const Temporaire& t) { return t.Obtenir() * 2; }
int main() {
int resultat = Calculer(Temporaire(5)); // Objet anonyme créé et utilisé ici.
return 0;
}
4.1 Optimisations du compilateur
Les compliateurs modernes optimisent souvent les constructions/copies successives, par exemple en fusionnant la construction d'un objet temporaire et sa copie dans une variable.
class Donnee {
public:
Donnee(int x) : _x(x) { /* Construction */ }
Donnee(const Donnee& autre) : _x(autre._x) { /* Copie */ }
// ...
private:
int _x;
};
Donnee CreerDonnee() {
Donnee temp(42);
return temp; // Optimisation possible : construction directe dans l'appelant.
}
int main() {
Donnee d = CreerDonnee(); // Peut n'impliquer qu'une seule construction.
return 0;
}