INF1010 – Contrôle périodique

Programming, Math, etc. · exam

Voir tous les documents en programmation

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;

};

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)

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

{

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;

}

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),

Publicité

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)

: 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;

Publicité

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

{

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

Publicité

class Processeur

{

public:

Processeur( unsigned int vitesse_ );

unsigned int get_vitesse() const;

private:

unsigned int vitesse;

};

class Ordinateur : public Appareil

{

public:

Ordinateur( string nom,

string date,

unsigned int vitesse );

private:

Processeur processeur;

};

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 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.

Appareil

Processeur

Ordinateur

Ecran

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

2

3

4

}

Portable lesPortables[10];

Ordinateur* ptr = new Ordinateur("ABC", "10/10/06", 2333);

cout << ptr->processeur.getVitesse() << endl;

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