INF1010 – Contrôle périodique
Automne 2006
Solutionnaire
1. Questions théoriques (3 pts)
1. (0.5 pt) Que signifie le mot const dans la ligne suivante :
void Polygone::afficher(ostream &out) const; Le const signifie que la méthode ne peut modifier aucun attribut de l’objet de type Polygone à partir duquel elle a été appelée. Par conséquence, elle n’a pas le droit non plus d’appeler des méthodes à partir de l’objet courant qui ne sont pas elles-mêmes constantes.
2. (0.5 pt) Quelle est l’utilité des membres protégés (protected) dans une classe ?
Les membres protégés restent inaccessibles aux classes et fonctions qui ne sont pas amies, sauf pour les classes dérivées. Ils permettent donc d’ouvrir une interface uniquement pour les classes dérivées.
3. (0.5 pt) Donnez une application du pointeur this.
- Appels en cascade - Vérification d’une affectation d’un objet vers lui-même - Accès à un attribut lorsqu’un paramètre ou une variable possède le même nom
4. (0.5 pt) Donnez un avantage et un inconvénient de l’utilisation d’une liste vis-à-vis d’un vecteur.
Avantage : L'ajout et retrait ont un cout fixe (ne dépend pas du nombre d’éléments) Inconvénient : Accès aléatoire impossible ; on doit parcourir séquentiellement les éléments.
5. (0.5 pt) Qu’est-ce qu’un argument générique non type ?
Un argument dont la valeur est générique, mais le type est fixe. Exemple : template <int ROWS>
6. (0.5 pt) À quoi sert le passage par référence constante ?
Sert à éviter une copie couteuse lors d’un passage, tout en s’assurant que l’objet ne sera pas modifié (simule donc un passage par valeur en évitant la copie couteuse qui peut être inutile)
Page 1 de 10
2. Composition d'objets (3 pts)
Donnez l'affichage que produit le code suivant:
class A { public: A(); ~A(); A( int a ); void afficher() const; private:
int att; };
A::A() {
cout << "A::A()" << endl; att = 0; }
A::A( int a ) {
cout << "A::A(int " << a << ")"
<< endl; att = a; }
A::~A() {
cout << "A::~A()" << endl; }
void A::afficher() const {
cout << "A::afficher(" << att << ")"
<< endl; }
class B { public: B(); B(int a); ~B(); A get\_att() const; void afficher() const; private:
A att; };
B::B() {
cout << "B::B()" << endl; }
B::~B() {
cout << "B::~B()" << endl; }
B::B( int a ) {
cout << "B::B(int " << a << ")"
<< endl; att = A(a); }
A B::get\_att() const {
return att; }
void B::afficher() const {
Publicité
cout << "B::afficher()" << endl; A a; a.afficher(); }
int main() {
cout << "debut" << endl; B b(2); cout << "ligne 1" << endl; b.afficher(); cout << "ligne 2" << endl; b.get\_att().afficher(); cout << "fin" << endl; }
debut A::A() B::B(int 2) A::A(int 2) A::~A() ligne 1 B::afficher() A::A() A::afficher(0) A::~A() ligne 2 A::afficher(2)
Page 2 de 10
A::~A() fin B::~B() A::~A()
3. Implémentation de classes et vecteur (4 pts)
a. (1 pt) Soit la classe Joueur possédant les attributs nom et numéro (le numéro du joueur dans l'équipe),
ainsi que les méthodes permettant de lire ces attributs et un constructeur qui crée un objet Joueur à partir
de son nom et de son numéro. Donnez la définition et l'implémentation de cette classe.
class Joueur { public:
Joueur(const string & n, int num); string get\_nom() const; int get\_numero() const;
bool operator<(const Joueur & j); private:
string nom; int numero; };
Joueur::Joueur(const string & n, int num)
: nom(n), numero(num) { }
string Joueur::get\_nom() const {
return nom; }
int Joueur::get\_numero() const {
return numero; }
b. (1 pt) Implémentez une méthode de la classe Joueur qui surcharge l'opérateur < (plus petit que) et qui
détermine si un joueur est plus petit qu'un autre joueur en fonction de leur numéro.
bool Joueur::operator<(const Joueur & j) {
return numero < j.numero; }
c. (2 pts) Implémentez l’interface de la classe Equipe ci-dessous (l’ordre des items dans le vecteur de
joueurs est sans importance).
class Equipe { public:
Equipe( const string& nom\_, Joueur& capitaine\_ ); void ajouter\_joueur( const Joueur& joueur ); void retirer\_joueur( const string& nom ); void afficher() const;
Page 3 de 10
private:
string nom; Joueur& capitaine; vector< Joueur > joueurs; };
Soit le programme suivant :
int main() {
Joueur david("david", 1); Joueur mohammed("mohammed", 8); Joueur michel("michel", 12);
Equipe bleus("les Bleus", david);
bleus.ajouter\_joueur(david); bleus.ajouter\_joueur(mohammed); bleus.ajouter\_joueur(michel); bleus.retirer\_joueur("david");
bleus.afficher(); }
L’exécution de ce programme doit afficher les lignes suivantes :
Equipe les Bleus capitaine: david michel 12 mohammed 8
class Equipe { public:
Equipe(const string & n, Joueur & cap); void ajouter\_joueur(const Joueur & j); void retirer\_joueur(const string & nom); void afficher() const; private:
string nom; Joueur & capitaine; vector< Joueur > joueurs; };
Equipe::Equipe(const string & n, Joueur & cap)
Publicité
: nom(n), capitaine(cap) { }
void Equipe::ajouter\_joueur(const Joueur & j) {
joueurs.push\_back(j); }
void Equipe::retirer\_joueur(const string & nom) {
for (size\_t i = 0; i < joueurs.size(); ++i) {
if (joueurs[i].get\_nom() == nom) {
joueurs[i] = joueurs[taille-1]; joueurs.pop\_back(); return; } } }
Page 4 de 10
void Equipe::afficher() const {
cout << "Equipe " << nom << endl; cout << "capitaine: " << capitaine.get\_nom() << endl;
size\_t taille = joueurs.size(); for (size\_t i = 0; i < taille; ++i) {
cout << joueurs[i].get\_nom() << " "
<< joueurs[i].get\_numero() << endl; } }
Page 5 de 10
4. Allocation dynamique (3 pts)
Soit les classes Individu et Famille suivantes :
class Individu {
public:
Individu( const string& prenom\_,
int age\_, Individu\ pere\_, Individu\ mere\_ ); string get\_prenom() const; Individu\ get\_mere() const; Individu\ get\_pere() const; void afficher() const; private:
int age; string prenom; Individu\ pere; Individu\ mere; };
Individu::Individu( const string& nom\_,
int age\_, Individu\ pere\_, Individu\ mere\_ ) : prenom(nom\_), age(age\_), pere(pere\_), mere(mere\_) { }
string Individu::get\_prenom() const {
return prenom; }
Individu\* Individu::get\_mere() const {
return mere; }
Individu\* Individu::get\_pere() const {
return pere; }
void Individu::afficher() const {
cout << prenom << " - " << age
<< " ans"; }
class Famille {
public:
Famille( const string& nom ); string get\_nom\_famille() const; void ajouter\_membre( Individu\* membre ); void afficher() const; private:
string nom\_famille; vector< Individu\* > membres; };
Famille::Famille(const string& nom)
: nom\_famille(nom) { }
string Famille::get\_nom\_famille() const {
return nom\_famille; }
void Famille::ajouter\_membre( Individu \* membre ) {
membres.push\_back(membre); }
void Famille::afficher() const {
Publicité
Individu\* ind; cout << "La famille " << nom\_famille
<< endl;
size\_t taille = membres.size(); for (size\_t i = 0; i < taille; ++i) {
membres[i]->afficher(); cout << endl;
ind = membres[i]->get\_pere(); if (ind != NULL) {
cout << " Pere: "; ind->afficher(); cout << endl; } ind = membres[i]->get\_mere(); if (ind != NULL) {
cout << " Mere: "; ind->afficher(); cout << endl; } } }
Page 6 de 10
a. (2 pts) Vous devez implémenter la fonction principale (main) qui crée une famille de cinq individus et
qui produit l’affichage suivant. Tout objet créé doit obligatoirement être alloué dynamiquement. Vous
devez utiliser le code fourni pour l’affichage.
La famille Girard Emile - 54 ans Berthe - 50 ans Eric - 33 ans
Pere: Emile - 54 ans Mere: Berthe - 50 ans Anne - 34 ans
Pere: Emile - 54 ans Mere: Berthe - 50 ans Maxime - 12 ans
Pere: Eric - 33 ans
int main() {
// à compléter... }
int main() {
Individu\ emile = new Individu("Emile", 54, 0, 0); Individu\ berthe = new Individu("Berthe", 50, 0, 0); Individu\ eric = new Individu("Eric", 33, emile, berthe); Individu\ anne = new Individu("Anne", 34, emile, berthe); Individu\ max = new Individu("Maxime", 12, eric, 0);*
Famille\ girard = new Famille("Girard");*
girard->ajouter\_membre(emile); girard->ajouter\_membre(berthe); girard->ajouter\_membre(eric); girard->ajouter\_membre(anne); girard->ajouter\_membre(max);
girard->afficher();
delete emile; delete berthe; delete eric; delete anne; delete max; delete girard; }
b. (1 pt) Expliquez pourquoi ce n'est pas la classe Famille qui est responsable de créer les individus (lors
de l'ajout d'un membre) et de les détruire.
Les individus sont créés dans le main, et doivent donc être détruits dans le main. Ce n’est donc pas une relation de composition (la famille ne possède pas d’individus) mais plutôt une relation d’agrégation (la famille connait un individu). Avec cette relation, on peut donc avoir qu’une personne appartient à plusieurs familles.
5. Héritage (5 pts)
Soit la hiérarchie de classes suivante :
Page 7 de 10
class Processeur { public:
Processeur( unsigned int vitesse\_ ); unsigned int get\_vitesse() const; private:
unsigned int vitesse; };
class Ecran { public:
Ecran( unsigned int taille\_ ); unsigned int get\_taille() const; private:
unsigned int taille; };
class Appareil { public:
Appareil( string nom\_,
string date\_ ); private:
string nom; string date; };
class Ordinateur : public Appareil { public:
Ordinateur( string nom,
string date, unsigned int vitesse ); private:
Processeur processeur; };
Publicité
class Portable : public Ordinateur { public:
Portable( string nom,
string date, unsigned int vitesse, unsigned int taille\_ecran ); private:
Ecran ecran; };
Page 8 de 10
1. (1 pt) Dessinez le diagramme de classes correspondant.
Processeur
Ecran
Appareil
Ordinateur
Portable
2. (2 pts) Implémentez tous les constructeurs des classes. Assurez-vous que, lors de la construction
d’un objet, tous les attributs soient initialisés, incluant ceux des objets qui composent l’objet créé.
Processeur :: Processeur ( unsigned int vitesse\_ ) : vitesse(vitesse\_) { } Ecran :: Ecran ( unsigned int taille\_ ) : taille(taille\_) { } Appareil :: Appareil ( string nom\_, string date\_ ) : nom(nom\_), date(date\_) { } Ordinateur :: Ordinateur ( string nom, string date, unsigned int vitesse\_ ) : Appareil(nom, date), vitesse(vitesse\_) { } Portable :: Portable ( string nom, string date, unsigned int vitesse, unsigned int taille\_ecran ) : Ordinateur(nom, date, vitesse), ecran(taille\_ecran) { }
3. (1 pt) Quels sont les constructeurs invoqués (dans l’ordre) lors de l’exécution de l’instruction
suivante :
Portable portable("A inc.", "05/09/06", 3100, 17);
Appareil( string nom\_, string date\_ ); Processeur( unsigned int vitesse\_ ); Ordinateur( string nom, string date, unsigned int vitesse ); Ecran( unsigned int taille\_ ); Portable( string nom, string date, unsigned int vitesse, unsigned int taille\_ecran );
4. Soit le programme principal suivant :
int main() { 1 Portable lesPortables[10]; 2 Ordinateur\* ptr = new Ordinateur("ABC", "10/10/06", 2333); 3 cout << ptr->processeur.getVitesse() << endl; 4 delete ptr;
}
(1 pt) Précisez quelle(s) instruction(s) est(sont) invalide(s) et pourquoi.
Page 9 de 10
- L’instruction 1 est invalide : le constructeur par défaut n‘existe pas - L’instruction 3 est invalide : On essaye d’accéder à un attribut privé (on a également
accepté que la fonction est non existante, puisque le nom n’était pas identique)
6. Programmation générique et conteneurs (2 pts)
1. (1 pt) Écrivez la fonction afficherVector qui prend un vecteur STL générique et qui affiche tous les
éléments de ce vecteur sur l’écran. Pour l’affichage, utilisez l’opérateur standard de sortie.
template <typename T> void afficherVector(const vector<T>& vecteur) {
for (size\_t i=0 ;i<vecteur.size() ;i++) {
cout << vecteur[i] << endl; } }
2. (1 pt) Peut-on passer à cette fonction un vecteur de n’importe quel type? Justifiez.
Il faut que le type contenu dans le vecteur soit « imprimable », c'est-à-dire que s’il s’agit d’une classe, la surcharge de l’opérateur << doit exister.
7. Question bonus (0.5 pt)
Que fait la fonction mystère suivante ?
void Fonction\_Mystere(int& a, int& b) {
a = a + b; b = a – b; a = a – b; }
Elle échange les valeurs de deux entiers, sans passer par un 3ème entier temporaire
Page 10 de 10