Contrôle périodique- Corrigé

Programming, Math, etc. · exam

Contrôle périodique- Corrigé

Automne 2007

1 Classes (5 points)

1. (0.5 pt) Définissez le concept d’encapsulation et donnez une motivation

pour son utilisation.

Le principe d’encapsulation stipule que tous les attributs d’un objet doivent

être privés, donc non visibles en dehors de la classe. Toute interaction doit

absolument se faire à travers l’interface de la classe. Une des motivations est

que l’encapsulation permet de garder le contrôle sur l’accès aux attributs.

2. (0.5 pt) Expliquez pourquoi, pour une classe générique, il n’est pas suffisant

d’inclure uniquement sa définition.

Le compilateur doit pouvoir s’assurer que toutes les opérations effectuées

sont valides pour le type en question. Cette vérification ne peut être faite

que lorsque l’on connaît le type qui remplace le type générique.

3. (0.5 pt) Donnez une utilisation possible du pointeur this.

Le pointeur this est utile pour implémenter une fonction qui peut être appe-

lée en cascade.

4. (0.5 pt) À quoi sert un membre protégé ?

Un membre protégé dans une classe de base sert à fournir un accès à toute

classe dérivée tout en interdisant l’accès aux restes des classes et fonctions.

5. (1pt) Pour une méthode donnée dans une classe de base, énoncez les trois

possibilités quant à l’implémentation de cette méthode dans une classe dé-

rivée.

On peut lui donner une nouvelle implémentation, étendre l’ancienne implé-

mentation (en faisant appel à la méthode de la classe de base) ou encore ne

pas la redéfinir.

6. (0.5 pt) Donnez la signature (prototype) de la surcharge d’opérateur d’af-

fectation de la classe Fraction en tant que fonction membre de la classe.

Fraction& Fraction : :operator =(const Fraction& droite) ;

7. (0.5 pt) Donnez la signature (prototype) de la surcharge d’opérateur += de

la classe Fraction en tant que fonction globale.

Fraction& operator +=(Fraction& gauche, const Fraction& droite) ;

8. (1.0 pt) Nous avons vu en cours trois relations possibles entre des classes.

Énumérez les avec une brève description.

L’héritage consiste à créer une classe qui sera une extension d’une autre

classe. La composition consiste à utiliser un objet comme attribut d’un autre

objet. L’agrégation consiste essentiellement en une utilisation d’un objet

comme faisant partie d’un autre objet.

1 de 10

Contrôle périodique- Corrigé

Automne 2007

2 Méthode de classe (2 points)

Un appel en casacade permet d’appeler une méthode plusieurs fois sur le même

objet. Soit la classe Point suivante :

int main()

{

Point P(3,4);

P.incrementer().incrementer().incrementer();

P.afficher();

}

La sortie du programme est:

(6,7)

class Point

{

public:

Point (int x, int y);

void afficher ();

?? incrementer(??);

private:

int x_,y_;

};

Point::Point(int x,int y)

{

Publicité

x_=x; y_=y;

}

void Point::afficher()

{

cout << "("<< x_<<","<<y_<<")"<< endl;

}

Écrivez l’implémentation de la fonction incrementer qui augmente d’une unité

les coordonnées (en x et en y) d’une instance de la classe Point et qui peut être

appelée en cascade.

Implémentation :

Point& Point::incrementer()

{

x_++ ;

y_++ ;

return *this ;

}

2 de 10

Contrôle périodique- Corrigé

Automne 2007

3 Programmation générique et passage de paramètres

(3 points)

Soit la classe générique Triplet dont la définition est incomplète et qui permet

de garder trois valeurs de types différents :

class Triplet

{

public:

Triplet(

getPremier(

getDeuxieme(

getTroisieme(

setPremier(

setDeuxieme(

setTroisieme(

private:

};

Soit la fonction principale suivante qui utilise une instance de la classe Triplet :

int main()

{

Triplet<int, string, double> Etudiant(1153152, "Jean Terrien", 18.5);

// Affichage de l’étudiant

cout << "Etudiant " << Etudiant.getPremier();

cout << " de matricule " << Etudiant.getDeuxieme();

cout << " a obtenu la note " << Etudiant.getTroisieme();

// Modification de la note de l’étudiant

Etudiant.setTroisieme(19.0);

}

Complétez la définition de la classe Triplet. On ne vous demande pas de four-

nir les implémentations de ses méthodes.

3 de 10

Contrôle périodique- Corrigé

Automne 2007

template <typename T1, typename T2, typename T3>

class Triplet

{

public:

Triplet(const T1& premier, const T2& deuxieme, const T3& troisieme);

T1 getPremier() const;

T2 getDeuxieme() const;

T3 getTroisieme() const;

void setPremier(const T1& premier);

void setDeuxieme(const T2& deuxieme);

void setTroisieme(const T3& troisieme);

private:

Publicité

T1 premier_;

T2 deuxieme_;

T3 troisieme_;

};

4 de 10

Contrôle périodique- Corrigé

Automne 2007

4 Concepts de base (7 points)

Soit la classe Etudiant qui représente un étudiant identifié par son matricule et

son nom. Voici sa définition et son implémentation :

class Etudiant

{

public:

Etudiant(int matricule, string nom)

: matricule_(matricule), nom_(nom) {}

int getMatricule() const { return matricule_; }

string getNom() const { return nom_; }

bool operator==(const Etudiant& etudiant) const

{

return matricule_ == etudiant.matricule_;

}

private:

int matricule_;

string nom_;

};

Soit la classe Base servant à garder une base de données d’étudiants. Toutes les

allocations de mémoire sont dynamiques. Voici sa définition :

class Base

{

public:

Base();

Base(const Base& base);

~Base();

Base& operator=(const Base& base);

bool ajouterEtudiant(int matricule, string nom);

string getNomEtudiant(int matricule) const;

bool retirerEtudiant(int matricule);

private:

vector<Etudiant*> etudiants_;

};

5 de 10

Contrôle périodique- Corrigé

Automne 2007

1. (1 pt) Expliquez pourquoi il est important d’implémenter le constructeur de

copie et de surcharger l’opérateur d’affectation.

Si le constructeur de copie ou l’opérateur d’affectation ne sont pas implé-

mentés, lorsqu’un objet de type Base est copié, leurs vecteurs sont copiés

élément par élément. Ainsi, les deux objets posséderont deux vecteurs qui

pointent vers les mêmes espaces mémoires. On aurait donc des espaces mé-

moires partagés, ce qui est problématique.

2. (0.5 pt) Expliquez pourquoi il est important d’implémenter le destructeur.

Si le destructeur n’est pas implémenté, il y aura fuite de mémoire lorsqu’un

objet de type Base est détruit, car les objets de type Etudiant n’auront pas

été détruits et sont perdus dans la mémoire.

3. (1 pt) Implémentez le destructeur.

Base::~Base()

{

for (size_t i = 0; i < etudiants_.size(); i++) {

delete etudiants_[i];

}

}

4. (2 pt) Implémentez la surcharge de l’opérateur d’affectation.

Base& Base::operator=(const Base& base)

Publicité

{

if (this != &base) {

for (size_t i = 0; i < etudiants_.size(); i++) {

delete etudiants_[i];

}

etudiants_.clear();

for (size_t i = 0; i < base.etudiants_.size(); i++) {

int matricule = base.etudiants_[i]->getMatricule();

string nom = base.etudiants_[i]->getNom();

Etudiant* e = new Etudiant(matricule, nom);

etudiants_.push_back(e);

}

}

return *this;

}

5. (1.5 pt) Implémentez la méthode ajouterEtudiant. Cette méthode doit

retourner false si le matricule de l’étudiant existe déjà, true sinon.

6 de 10

Contrôle périodique- Corrigé

Automne 2007

bool Base::ajouterEtudiant(int matricule, string nom)

{

for (size_t i = 0; i < etudiants_.size(); i++) {

if (etudiants_[i]->getMatricule() == matricule) {

return false;

}

}

etudiants_.push_back(new Etudiant(matricule, nom));

return true;

}

6. (1 pt) Implémentez la méthode getNomEtudiant qui recherche le nom

de l’étudiant en ayant seulement son matricule. Cette méthode doit retourner

une chaîne de caractères vide si l’étudiant n’est pas trouvé.

string Base::getNomEtudiant(int matricule) const

{

for (size_t i = 0; i < etudiants_.size(); i++) {

if (etudiants_[i]->getMatricule() == matricule) {

return etudiants_[i]->getNom();

}

}

return "";

}

7 de 10

Contrôle périodique- Corrigé

Automne 2007

5 Constructeurs (3 points)

Soient les classes et le programme suivant :

class A

{ public:

A();

A(int n);

private:

int attrA_;

};

A::A():attrA_(0)

{ cout << "A()"<< endl;

}

class B: public A

{ public:

B();

B(int n1, int n2);

private:

int attrB_;

Publicité

};

B::B(): attrB_(0)

{ cout << "B()"<< endl;

}

A::A(int n):attrA_(n)

{ cout << "A("<< attrA_<< ")"<< endl;

}

B::B( int n1, int n2):A(n1),attrB_(n2)

{ cout << "B("<< attrB_<< ")"<< endl;

}

class C: public B

{

public:

C();

C(int n1, int n2);

private:

B attrC_;

};

C::C()

{ cout << "C()"<< endl;

}

int main()

{

C ObjetC(10,20);

}

C::C(int n1, int n2): B(n1,n2)

{

cout << "C("<< n1 <<","<< n2<< ")"<< endl;

}

1. (1 pt) Dessinez le diagramme de classe correspondant.

8 de 10

Contrôle périodique- Corrigé

Automne 2007

2. (2 pts) Quel sera l’affichage si on exécute ce programme ?

A(10)

B(20)

A()

B()

C(10,20)

9 de 10

Contrôle périodique- Corrigé

Automne 2007

6 Bonus (+1 point)

Implémentez la méthode retirerEtudiant de la classe Base. L’ordre des

objets dans le vecteur n’est pas important.

bool Base::retirerEtudiant(int matricule)

{

for (size_t i = 0; i < etudiants_.size(); i++) {

if (etudiants_[i]->getMatricule() == matricule) {

delete etudiants_[i];

etudiants_[i] = etudiants_[etudiants_.size() - 1];

etudiants_.pop_back();

return true;

}

}

return false;

}

10 de 10