Exam Corrig
Exam Corrig
Exam Corrig
Mohamed EL ANSARI
Enseignant Chercheur
Département d’Informatique
Faculté des Sciences, Université Ibn Zohr
Agadir
Ce document présente la solution des examens / contrôles proposés, en programmation
orientée objet C++, pour les filières Sciences Mathématiques et Applications (SMA) et
Sciences Mathématiques et Informatique (SMI) entres les années 2005 et 2010. Le cours
est dispensé en semestre 5 pour les deux filières.
Exercice 1
Soit le programme C++ suivant:
#include <iostream.h>
main() {
int x[3] = { 3, 6, 11 }; int y = 4;int &z = y;
cout << "1," << x[0] <<","<< x[1] <<","<< y <<","<< z<< endl;
int * p = &x;int * p = x; *p = 5;
cout << "2," << x[0] <<","<< x[1] <<","<< y<<","<< z << endl;
int * q = &y;int * q = y;*q = 7;
cout << "3," << x[0] <<","<< x[1] <<","<< y <<","<< z <<endl;
q = new int; if (q!=NULL) *q = 2; delete q;
cout << "4," <<x[0] <<","<< x[1] <<","<< y <<","<< z << endl;
*(++p) = 8;
cout <<"5," << x[0] <<","<< x[1] <<","<< y <<","<< z << endl;
z =*(x+2)-2;
cout << "6," << x[0] <<","<< x[1] <<","<< y <<","<< z <<endl; getchar();
}
1. Quel est le nom du rapport entrenu par les variables z et y ? par les variables p et
x?
Exercice 2
Donner la sortie du programme C++ suivant. Préciser le nombre d’objets créés et celui
d’objets détruits.
#include<iostream.h>
class point{ int abs, ord;
public:
point(){
cout<<"++ point par defaut"<<endl; abs=ord=0;
}
point(int a, int b){
cout<<"++ point normal"<<endl; abs=a; ord=b;
}
Exercice 3
Définir une classe Cha^
ıne utilisable pour manipuler des chaı̂nes de caractères et dont la
seule donnée membre est un tableau dynamique de caractères (pointeur), tab. On la
munira des méthodes suivantes :
• un constructeur de copie,
• une méthode concat qui reçoit en paramètre une Chaı̂ne C et retourne une nouvelle
Chaı̂ne dont la donnée membre est la concaténation de tab et de la chaı̂ne de
caractères de C,
• int * p = &x;
• int * q = y;
1,3,6,4,4
2,5,6,4,4
3,5,6,7,7
4,5,6,7,7
5,5,8,7,7
6,5,8,9,9
Exercice 2
• La sortie du programme est :
Les deux points pointés par les deux pointeurs d et e ne se sont pas détruits. Il faut
expliciter leurs destruction en utililisant delete
class chaine{
char * tab;
public :
chaine(char *s=""){
tab = new char[strlen(s)+1];
strcpy(tab,s);
}
~chaine(){
delete tab;
}
int length(){
return strlen(tab);
}
void affiche(){
cout<<tab<<endl;
}
Exercice 1 (16pts):
Réaliser une classe pile nommée ensemble_entiers permettant de manipuler des
ensembles de nombres entiers. Leséléments seront conservés dans un tableau qui sera
alloué dynamiquement par le constructeur de la classe, et qui sera détruit par le
destructeur. Les données membres permettront de connaı̂tre :
• l’adresse du tableau (int *adr),
• le nombre maximum d’éléments du tableau (int dim),
• et le nombre courant d’éléments dans le tableau (int nelem).
La classe comportera les fonctions membres suivantes:
• ensemble entiers(int n): constructeur allouant dynamiquement un
emplacement de n entiers. Si la valeur de n n’est pas fournie, le constructeur
allouera par défaut un emplacement de vingt entiers,
• ensemble entiers(ensemble entiers & e):constructeur de recopie,
• ensemble entiers(): destructeur;
• void empile(int p): ajoute l’entier p sur la pile;
• int depile(): fournit la valeur de l’entier situé en haut de la pile, en le supprimant
de la pile,
• int pleine(): fournit la valeur 1 si la pile est pleine, 0 sinon,
• int vide(): fournit 1 si la pile est vide; 0 sinon,
• int appartient(int p): fournit 1 si l’entier p appartient à la pile, sinon.
Surcharge des opérateurs: Introduisez les opérateurs >, < et + tels que si p, q et r sont
des objets de type ensemble entiers et n une variable entière:
• p<n ajoute la valeur de n sur la pile p (en ne renvoyant aucune valeur),
• p>n supprime la valeur du haut de la pile;
• r=p+q; r sera la concaténation des deux objets p et q.
On prévoira pour cela trois fonctions amies.
Comment pourrait-on adapter la classe ensemble entiers pour qu’elle dispose d’une
fonction membre nbre pile fournissant le nombre d’objets créés à un instant donné.
Ecrire un petit programme d’utilisation permettant de rentrer au clavier 20 nombres
entiers quelconques et de les stocker dans un objet de type ensemble entiers.
class ensemble_entiers{
int dim;
int *adr;
int nelem;
static int nbobjets;
public:
ensemble_entiers(int n=MAX){
adr=new int[dim=n];
nelem=0;
nbobjets++;
}
ensemble_entiers(ensemble_entiers & p){
adr=new int [dim=p.dim];
nelem=p.nelem;
int i;
for(i=0;i<nelem;i++) adr[i] = p.adr[i];
nbobjets++;
}
~ensemble_entiers(){delete [] adr; nbobjets--;}
void empile(int p){
if (nelem < dim) adr[nelem++] = p;
}
int depile(){if (nelem > 0) return adr[--nelem];
else return 0;
}
int pleine(){return (nelem==dim);}
int vide(){return (nelem == 0);}
main(){
ensemble_entiers t(20);
int val,i;
for(i=0;i<20;i++)
{
cin>>val;
t.empile(val);
}
}
Exercice 2
#include<iostream.h> #define mftion(x) x*(x+1)
inline int iftion(int x){return (x*(x+1));}
main(){
int a=3;
cout<<mftion(a+3)<<endl; // ---> 24
cout<<iftion(a+3)<<endl; // ---> 42
}
Exercice 1
Soit les deux classes suivantes :
class A {
protected :
int i;
public :
A(int k) : i(k) {cout<<"A constructed "<<i<<"\n";}
~A() { cout<<"A deleted\n"; }
};
class B : public A {
private :
int j;
public :
B(int k, int n = 1) : A(k), j(n) { cout<<"B constructed "<<j<<"\n"; }
~B() { cout<<"B deleted\n"; }
};
void main() {
A *a, *c, *d;
B b(20,30), *e;
a = new A(10);
c = new A(15);
e = new B(2,3);
delete a;
delete e;
}
Exercice 2
Considérons le programme suivant
#include<iostream.h>
#include"Complex.h"
2.1 + 3.15 i
Le module de k : 3.78583
3.1 + 1.15 i
5.4 + -5.05 i
L’argument de p : -0.751918
Donner les deux fichiers Complex.h et Complex.cpp pour que le programme ci-dessus
donne le résultat attendu.
Indications:
1. Un nombre complexe est défini comme un couple de nombres réels, le premier appelé
partie réelle et le second partie imaginaire. Par exemple 10 + 4 i est un nombre
complexe dont la partie réelle est 10 et la partie imaginaire est 4. Par convention,
on note i le complexe dont la partie réelle est 0 et la partie imaginaire est 1. Sa
particularité est que son carré vaut -1 (i2 = 1).
3. afficheComplex (ligne 5) est une fonction membre de la classe Complex dont le but
l’affichage de l’objet appelant. Lors de sa définition respectez le format d’affichage
mentionné dans l’éxécution.
5. Pour que le programme acceptera les instructions figurant sur les lignes 7 et 9
vous devez surcharger les opérateurs + et *. Pour cela, définissez les fonctions
correspondantes.
Exercice 2
#include<iostream.h>
#include<cmath>
// ------------------------- Complex.h ---------------------
class Complex{
double re, im;
public:
Complex(double vre=0, double vim=0);
~Complex();
void afficheComplex();
double getModule();
double getArgument();
Complex operator+(Complex a);
Complex operator*(Complex a);
};
Complex::~Complex(){} // facultatif
void Complex::afficheComplex(){
cout<<re<<" + "<<im<<" i"<<endl;
double Complex::getModule(){
return sqrt(re*re+im*im);
}
double Complex::getArgument(){
if (re!=0) return atan(im/re);
if (im >0) return pi/2;
if (im < 0) return -pi/2;
return 0;
}
void main() {
Complex i;
Complex j(1,-2);
Complex k(2.1,3.15);
k.afficheComplex();
cout<<"Le module de k : "<<k.getModule()<<endl;
i = j + k;
i.afficheComplex();
Complex p = i*j;
p.afficheComplex();
cout<<"Largument de p : "<<p.getArgument()<<endl;
}
Exercice 1
Soient les fonctions suivantes :
int clone(int n){cout << "\nLa fonction I = "; return n*n;}
double clone(int n, double d){cout <<"\nLa fonction III = "; return n*d;}
int main() {
cout << clone(3) << endl;
cout << clone(clone(2),3.0) << endl;
}
Exercice 2
Ecrire la macro-fonction (#define) qui calcule la factorielle de n. Puis la fonction inline
qui fait de même.
Montrer et expliquer sur un exemple que l’appel n’est pas réalisé correctement avec la
macro-fonction alors qu’il l’est avec la fonction inline.
• Ecrire un programme faisant appel à ladite fonction dans les deux cas.
3. Le résultat d’exécution:
La fonction I = 9
La fonction I =
La fonction III = 12
Exercice 2
• La macro-fonction:
• LA fonction inline:
Cela est parfaitement normal. En effet, faisons à la main les remplacements effectués par
le prprocesseur (souvenez-vous, c’est du ”copier-coller” !). Nous aurions dans un premier
temps :
... et cela de manière infinie !!! Ce qui est impossible; compiler un fichier de taille infinie
ne veut rien dire ! Par conséquent, le préprocesseur indique que la macro-fonction factM
n’est jamais entièrement définie et qu’il s’agit donc d’une erreur.
Exercice 3
#include<iostream>
#include<cstdlib>
using namespace std;
// Programme test
main()
{
int tab[5] = {2,0,-5,8,41};
int minimum, position;
min_tab_ref(tab, 5,minimum, position);
cout<<"minimum = "<<minimum<<" position = " <<position<<endl;
// Sortie du programme
minimum = -5 position = 2
minimum = -5 position = 2
Exercice 1
a) Donner la sortie du programme C++ suivant et commenter en précisant notamment
le nombre d’objets créés et détruits et la nature (plantage ou pas) de la terminaison de
l’exécution.
#include <iostream.h>
class Machin {
public :
int bidule ;
Machin(int b) { cout <<" ++ Machin normal " << endl ; bidule = b; }
~Machin() { cout <<" -- Machin normal " << endl ; }
} ;
class Truc {
public :
Machin * machin ;
Truc(Machin * m) { cout <<" ++ Truc normal " << endl ;
machin = m ;
}
~Truc() { cout << "--Truc normal "<< endl ;
if (machin != NULL) delete machin ;
machin= NULL ; }
} ;
main() {
Machin m(1) ;
Truc x(&m);
Truc y(x);
}
Machin(Machin & m) {
cout << "++ Machin recopie " << endl ;
bidule = m.bidule ; }
main(){
POINT a(7,7), b; // creation de deux points a et b
a.affp();cout<<endl; // affichage du point a
SEGMENT s1(1,2,6,9); // creation du segment s1
s1.affs(); cout<<endl; // affichage du segment s1
SEGMENT s2(a,b); // creation du segment s2
s2.affs(); cout<<endl; // affichage du segment s2
Compléter le programme ci-dessus pour qu’il fonctionne correctement. Vous allez définir
les classes POINT et SEGMENT ainsi que les fonctions nécessaires. Un programme complet
donnera la sortie suivante:
point : (7,7)
segment : point : (1,2) point : (6,9)
segment : point : (7,7) point : (0,0)
le point (4,4) appartient au segment s1
le nombre de points crees : 5
le nombre de segments crees : 2
++ Machin normal
++ Truc normal
--Truc normal
-- Machin normal
Truc(Truc & t) {
cout << ++ Truc recopie << endl ;
machin = new Machin(t.machin->bidule) ;
}
La sortie deviendra :
++ Machin normal
++ Truc normal
++ Truc recopie
++ Machin normal
--Truc normal
-- Machin normal
--Truc normal
-- Machin normal
++ Machin normal
++ Truc normal
++ Machin normal
++ Truc recopie
++ Machin normal
--Truc normal
-- Machin normal
--Truc normal
-- Machin normal
-- Machin normal
c) .....
Exercice 2
#include <iostream>
using namespace std;
class SEGMENT;
class POINT{
public:
POINT (int x= 0, int y=0):X(x),Y(y){nbre_points++;}
POINT(const POINT &p):X(p.X), Y(p.Y){}
int Abscisse() const{return X;}
int Ordonnee() const {return Y;}
void affp(){cout<<"point : ("<<X<<","<<Y<<")";}
static int nombre_points(){return nbre_points;}
friend bool appartient(POINT p, SEGMENT s);
private:
int X;
int Y;
static int nbre_points;
};
int POINT::nbre_points = 0;
class SEGMENT{
POINT debut, fin;
static int nbre_segments;
public:
SEGMENT(int x1=0, int y1=0, int x2=0, int
int SEGMENT::nbre_segments = 0;
if (p.Y == a * p.X + b)
if ((p.X<=s.debut.X && p.X>=s.fin.X) || (p.X>=s.debut.X
&& p.X<=s.fin.X))
return true;
return false;
}
main(){
POINT a(7,7), b; // creation de deux points a et b
a.affp();cout<<endl; // affichage du point a
SEGMENT s1(1,2,6,9); // creation du segment s1
s1.affs(); cout<<endl; // affichage du segment s1
SEGMENT s2(a,b); // creation du segment s2
s2.affs(); cout<<endl; // affichage du segment s2
• un constructeur de copie,
• une méthode concat qui reçoit en paramètre une Chaı̂ne C et retourne une nouvelle
Chaı̂ne dont la donnée membre est la concaténation de tab et de la chaı̂ne de
caractères C,
• une mthode compare permettant de comparer deux chaı̂nes. Elle retourne true (1)
si les deux chaı̂nes sont égales et false (0) le cas contraire.
Que se passe-t-il lorsqu’un appel est fait avec comme arguments deux short.
Pourquoi ?
Un short étant par définition un entier sur 16bit, il est possible de convertir sans perte
d’information un short en int. Par conséquent, lors d’un appel avec comme arguments
deux short, le compilateur va choisir la fonction int add(int, int).
Que se passe-t-il lorsqu’un appel est fait avec un int et un float ?
Confusion entre les 2 premières fonctions et par conséquent le compilateur génère une
erreur au moment de la compilation.
Donner un programme faisant appel aux différentes fonctions.
void main()
{
// addition de 2 entiers (int)
int ia = 1;
int ib = 2;
tab tr;
add(ta, tb, tr);
...
Exercice 2
Même solution que celle donnée en exercice 2 page 20.
Exercice 3
Même solution que celle donnée en exercice 3 page 7.
Exercice 1: (15pts)
Définir une classe Chaine utilisable pour manipuler des chaı̂nes de caractères et dont la
seule donnée membre est un tableau dynamique tab de caractères. On la munira des
méthodes suivantes :
2. un constructeur de recopie,
5. une méthode reverse permettant de reverser la donnée membre tab, par exemple,
la chaı̂ne maison deviendra nosiam,
9. une méthode compare permettant de comparer deux chaı̂nes. Elle retourne true (1)
si les deux chaı̂nes sont égales et false (0) le cas contraire.
11. une fonction lengthmax permettant d’obtenir parmi deux chaı̂nes, celle qui a la plus
grande longueur.
Ecrire un petit programme d’application qui déclare des instances de la classe Chaine
sur lesquelles vous allez appliquer les différentes fonctions définies ci-dessus (fonctions
membres et fonctions surchargées).
#include<iostream>
using namespace std;
int x = 6;
int h(int & x){x = 2*x; return x;}
int g(int m){return x++;}
int & f(int &x){x+=::x; return x;}
int main()
{
int x = -1;
f(::x) = h(x);
cout<<f(x)<<" "<<g(x)<<" "<<h(x)<<" "<<x<<"
"<<::x<<endl;
f(::x) = g(x);
cout<<f(x)<<" "<<g(x)<<" "<<h(x)<<" "<<x<<"
"<<::x<<endl;
return 0;
}
class chaine{
char *tab;
public:
int length(){return strlen(tab);}
chaine(char*_tab = ""){
tab = new char[strlen(_tab)+1];
strcpy(tab,_tab);
}
chaine(chaine & t){
tab = new char[strlen(t.tab)+1];
strcpy(tab, t.tab);
}
~chaine(){delete [] tab;}
void affiche(){cout<<tab;}
void reverse(){
int taille = strlen(tab);
char c;
for(int i=0;i<taille/2;i++)
{
c=tab[i];
tab[i]=tab[taille-1-i];
tab[taille-1-i] = c;
}
}
bool compare(chaine & c){
int i, t1=strlen(tab), t2=c.length(), min;
min = t1<t2? t1 : t2;
if (t1 != t2) return 0;
else for(i=0;i<min;i++) if (tab[i] != c.tab[i]) return 0;
return 1;
}
chaine lengthmax(chaine c){
if (strlen(tab)>c.length()) return *this; else return c;
}
friend chaine operator+(chaine c1, chaine c2);
friend istream & operator>>(istream & i, chaine & c);
friend ostream & operator<<(ostream & o, chaine &c);
int main()
{
chaine ch1("Mohamed "), ch4("EL ANSARI");
chaine ch2(ch1);
ch1.affiche();cout<<endl;
ch2.affiche();cout<<endl;
cout<<ch1.length();cout<<endl;
ch2.reverse(); ch2.affiche(); cout<<endl;
chaine ch3=ch1+ch4;
ch3.affiche(); cout<<endl;
cin>>ch2;
ch2.affiche(); cout<<endl;
cout<<ch2<<endl;
cout<<ch1.compare(ch2)<<endl;
cout<<ch1.lengthmax(ch4)<<endl;
return 0;
}
Exercice 2
-5 -2 -4 -2 -2
-10 -1 -10 -5 -1