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