Vous êtes sur la page 1sur 4

Université Pierre et Marie Curie Master M1 "Mathématiques et Applications"

Période 2 - 2010/2011 Programmation en C/C++


Examen du 4 mai 2011 - Corrigé de la question de cours
Deux heures - Sans document.

Exercice 1 (Questions de cours)


1. Ces 2 symboles correspondent en C++ à des opérateurs binaires.
Le symbole >> correspond à l’opérateur d’extraction : il agit entre une variable (ou un objet) à sa gauche et
un flux d’entrée (comme std::cin) à sa droite.
Le symbole << correspond à l’opérateur d’injection : il agit entre un flux de sortie (comme std::cout) à sa
gauche et une variable (ou un objet) à sa droite.
Remarque (hors barème) : entre 2 entiers ces symboles correspondent (en C et C++) aux opérateurs de décalage
de bit.
2. L’opérateur d’injection peut être surchargé pour un objet obj d’une classe CL existente en utilisant une fonction
globale (non membre de la classe CL) dont le prototype est
std :: ostream & operator < <( std :: ostream & o , CL const & obj );

L’opérateur d’extraction peut être surchargé pour un objet obj d’une classe CL existente en utilisant une
fonction globale (non membre de la classe CL) dont le prototype est
std :: istream & operator > >( std :: istream & i , CL & obj );
L’en-tête à inclure est iostream.

3. Plusieurs façons de faire : soit surcharger l’opérateur << de façon générique, soit écrire une fonction affiche. Un
exemple de fonction générique affiche. Ici il suffit que le type des données soit générique.
template < typename T >
2 void affiche ( std :: vector <T > const & v ) {
for ( int i = 0; i < v . size (); ++ i )
4 std :: cout << v [ i ] << std :: endl ;
}

4. Cette fois c’est un peu plus délicat car il faut une fonction générique qui s’applique aux deux types de conteneur
(vector et list).
template < typename T >
2 void affiche ( T const & v ) {
typename T :: const_iterator it ;
4 for ( it = v . begin (); it != v . end (); ++ it )
std :: cout << * it << std :: endl ;
6 }

(Les points sont donnés même si le mot-clé typename -pourtant indispensable car T est générique- est oublié).

Exercice 2
1. Remarques :
– il n’y a pas de type bool en C(à la place on utilise une variable entière et on lui fait prendre 2 valeurs : 0 pour
faux et 1 pour vrai),
– l’opérateur de comparaison est == et non =,
– la boucle interne doit s’arrêter à l’avant-dernier élément, c’est à dire celui d’indice n-2.
Voici une version pour un tableau t d’entiers de taille n.
void tri_bulle ( int t [] , int n ) {
2 int j , tmp , en_desordre ;
do {
4 en_desordre = 0;
for ( j = 0; j < n -1; ++ j ) {
6 if ( t [ j ] > t [ j +1]) {
tmp = t [ j ];
8 t [ j ] = t [ j +1];
t [ j +1] = tmp ;

1
10 en_desordre = 1;
}
12 }
} while ( en_desordre == 1);
14 }

2. Il faut rajouter un compteur k qui compte le nombre de fois qu’on a executé la boucle externe do...while.
void tri_bulle_opt ( int t [] , int n ) {
2 int j , tmp , en_desordre ;
int k = 0;
4 do {
en_desordre = 0;
6 for ( j = 0; j < n -k -1; ++ j ) {
if ( t [ j ] > t [ j +1]) {
8 tmp = t [ j ];
t [ j ] = t [ j +1];
10 t [ j +1] = tmp ;
en_desordre = 1;
12 }
}
14 k ++;
} while ( en_desordre == 1);
16 }

3. Un tableau dynamique se crée avec la fonction malloc en C.


int main ( void ) {
2 int n , * tab ;
printf ( " Entrer un entier positif " );
4 scanf ( " % d " , & n );
tab = malloc ( n * sizeof ( int ));
6 for ( i = 0; i < n ; ++ i )
tab [ i ] = random ();
8 tri_bulle_opt ( tab , n );
return 0;
10 }

4. struct maillon {
2 int data ;
struct maillon * suivant ;
4 struct maillon * precedent ;
}

5. Pour échanger le maillon (à l’adresse) m et le maillon suivant m->suivant dans une liste doublement chaînée il
faut modifier les 4 maillons aux adresses m->precedent, m, m->suivant, m->suivant->suivant (faire un dessin). On
appelle ces 4 maillons ai, b, c et d et on modifie les liens (faire un dessin). Remarque importante : les adresses
a et d peuvent être l’adresse NULL.
void swap ( struct maillon * m) {
2 struct maillon * a = m - > precedent ;
struct maillon * b = m;
4 struct maillon * c = m - > suivant ;
struct maillon * d = m - > suivant - > suivant ;
6

// modification du maillon qui precede m ( s ’ il existe !)


8 if ( a != NULL ) a - > suivant = c ;
// modification du maillon qui suit m
10 c - > precedent = a ;
c - > suivant = b ;
12 // modification du maillon m
b - > precedent = c ;
14 b - > suivant = d ;
// modification du maillon d ( s ’ il existe ...)
16 if ( d != NULL ) d - > precedent = b ;
}

2
6. Une façon de faire est de placé le pointeur dernier à la fin de la liste (sur le dernier maillon) puis de trier
jusqu’à ce pointeur, puis de le faire avancer à chaque itération de la boucle do...while. On peut aussi utiliser
un compteur et la longueur de la liste...
void t ri _ b ul le_opt_liste ( struct maillon * liste ) {
2 struct maillon * pt = NULL ;
struct maillon * dernier = NULL ;
4 while ( dernier - > suivant != NULL )
dernier = dernier - > suivant ;
6 int en_desordre ;
do {
8 en_desordre = 0;
for ( pt = liste ; pt - > suivant != dernier ; pt = pt - > suivant ) {
10 if ( pt - > data > pt - > suivant - > data ) {
swap ( pt );
12 en_desordre = 1;
}
14 }
dernier = dernier - > precedent ;
16 } while ( en_desordre == 1);
}

Exercice 3
1. Les méthodes operator() et derivee sont qualifiées de « virtuelles pures ». La classe FctR est donc une classe
abstraite dont le but est d’être une classe mère afin d’utiliser le mécanisme de polymorphise dynamique : un
objet d’une classe fille à une adresse compatible avec l’adresse de la classe FctR.
Ces méthodes doivent être redéfinies dans les classes filles.
2. Il ne faut pas oublier de définir le constructeur !
struct Cubique : public FctR {
2 Cubique ( double a , double b ) : a ( a ) , b ( b ) {};
double operator ()( double x ) const {
4 double y = a * x + b ;
return y * y * y ;
6 };
double derivee ( double x ) const {
8 double y = a * x + b ;
return 3* a * y * y ;
10 };
private :
12 double a , b ;
}

3. – ligne 4 : fct(f)
– ligne 8 : *this
– ligne 10 : protected:
Il est nécessaire de définir fct comme une adresse de FctR pour plusieurs raisons : pour utiliser le polymorphisme
dynamique mais aussi parce que FctR est une classe abstraite et ne peut donc pas avoir d’objets. L’instruction
FctR fct est donc impossible et ce quelque soit le contexte.

4. Attention le constructeur de la classe Newton doit appeler explicitement le constructeur de la classe mère
AlgorithmeR.
class Newton : public AlgorithmeR {
2 public :
Newton ( double x_0 , FctR & f ) : AlgorithmeR ( x_0 , f ) {};
4 friend std :: ostream & operator < <( std :: ostream & o , Newton & const algo );
private :
6 void calcul_iter () {
x_n = x_n - fct ( x_n ) / fct . derivee ( x_n );
8 }
}
10

std :: ostream & operator < <( std :: ostream & o , Newton & const algo ) {
12 return o << algo . n_iter << " \ t " << algo . x_n ;
}

3
5. class Secante : public AlgorithmeR {
2 public :
Secante ( double x_0 , FctR & f ) : AlgorithmeR ( x_0 , f ) , x - nm1 ( x_0 -1) {};
4 private :
double x_nm1 ;
6 void calcul_iter () {
double sauve = x_n ;
8 x_n = x_n - ( x_n - x_nm1 )/( fct ( x_n ) - fct ( x_nm1 )) * fct ( x_n );
x_nm1 = sauve ;
10 }
}

Vous aimerez peut-être aussi