class boundedStack: public boundedSequence {
public:
boundedStack(unsigned);
void put(int);
bool full() const;
bool empty() const;
int get();
private:
int *_sp;
unsigned _free;
};
boundedStack::boundedStack(unsigned n)
:boundedSequence(n) // Il indiquer les parametres du constructeur de la classe de base.
{
// Inutile d'allouer le tableau puisque c'est fait dans le constructeur de la classe de base
_sp = _data;
_free = n;
}
void boundedStack::put(int i)
{
assert(!full());
*_sp++ = i;
_free--;
}
bool boundedStack::full() const
{
return !_free;
}
bool boundedStack::empty() const
{
return _sp == _data;
}
int boundedStack::get()
{
assert(!empty());
_free++;
return *--_sp;
}
class boundedQueue: public boundedSequence {
public:
boundedQueue(unsigned);
void put(int);
bool full() const;
bool empty() const;
int get();
private:
int *_insertion;
int *_deletion;
int *const _last;
int _free;
};
boundedQueue::boundedQueue(unsigned n)
:boundedSequence(n), _last(_data + n)
{
_insertion = _deletion = _data;
_free = n;
}
void boundedQueue::put(int i)
{
assert(!full());
*_insertion++ = i;
if (_insertion >= _last)
_insertion = _data;
_free--;
}
bool boundedQueue::full() const
{
return !_free;
}
bool boundedQueue::empty() const
{
return _insertion == _deletion;
}
int boundedQueue::get()
{
assert(!empty());
_free++;
int res = *--_deletion;
if (_deletion < _data)
_deletion = _last;
return res;
}
Parmi les erreurs à ne pas faire pour ne pas perdre de points:
Je n'ai pas pinaillé sur les indices qui tombaient un cran trop tot ou trop tard, ni sur les const manquants dans full() et empty() (bien que ce soit une erreur fréquente et difficile à détecter en pratique), les décalages inutiles...
L'idée que j'avais en tête pour les files était un buffer circulaire, mais beaucoup l'ont implémenté avec des décalages, ce qui était correct (pourvu que les décalages soient cohérents), quoique moins efficace en terme de CPU. En revanche, ceux qui ne géraient pas de buffers circulaires ni de décalage perdaient un point (après un nombre fini d'insertions et de suppressions, la file bloque alors qu'il y a encore de la place disponible).
Les méthodes devaient être friend
de leurs classes
respectives (1 point), il fallait implémenter les méthodes de façon
correcte relativement à la définition que vous aviez donné des classes
correspondantes (1 point). Enfin, il fallait utiliser la méthode
display
qui était fournie dans la classe
BoundedSequence
(1 point). Tous ces points s'entendent
sous réserve qu'il n'y ait pas d'énormités ou d'erreurs, pour lesquelles
j'ai retiré des 1/2 points, par exemple:
display
devrait être friend", alors qu'elle était
protected
, précisément pour pouvoir s'en servir dans les
classes dérivées.
stream << b.display()
alors que display
est void
.
sequence._data
, et non
_data
).
#include <iostream.h>
struct c {
int i;
c(int) { i = 5; }
c(float) { cout << 17 << ' '; }
~c() { cout << 43 << ' '; }
void operator=(int) { i = 7; }
int m() { return 11; }
};
struct d: public c {
d(): c('a') { }
d(int): c(1.0f) { cout << 19 << ' '; }
~d() { cout << 41 << ' '; }
int m() { return 13; }
int n() const { return 31; }
int n() { return 37; }
};
ostream &operator<<(ostream &s, const d&)
{
s << 23 << ' ';
}
main()
{
c x(1);
cout << "a: " << x.i << endl;
Réponse:
a: 5
Tout le monde a répondu correctement.
Réponse:
b: 5
c y = 2;
cout << "b: " << y.i << endl;
Il fallait ici reconnaître qu'il s'agit d'un constructeur, et non
d'une affectation.
Réponse:
c: 11
d z;
c &r = z;
cout << "c: " << r.m() << endl;
La méthode n'est pas virtuelle dans c, ici on a une
référence sur c, c'est donc cette méthode (celle de c) qui
joue.
Réponse:
d: 17 19 23 41 43
cout << "d: " << d(1) << endl;
Il ne fallait pas oublier l'appel à <<. Les puristes auraient
pu faire remarquer que, comme il n'y a pas de return dans
l'opérateur, le comportement est indéfini (j'ai accepté cette réponse). Il ne fallait pas oublier
les destructeurs (ceux qui n'ont mis que 17 19 23 avaient
néanmoins 1/2 point). Je n'ai pas tenu compte de l'ordre des
destructeurs.
Réponse:
e: 37
cout << "e: " << z.n() << endl;
Un point délicat de l'exercice. La bonne justification est
que le profil de l'opérateur est operator<<(ostream &,
int);
; il aurait fallu que ce soit const int pour que
l'autre option soit prise. Le fait que t et const t
soient des types distincts est un des éléments gênants de C++.
Et il ne fallait pas oublier:
41 43 43 43, les destructeurs de d, y, et x, respectivement. J'étais
tellement content de les voir que j'ai donné le point même à ceux qui
ne les ont pas mis dans l'ordre.
}
1. char *p;
2. const char *q;
3. char &r;
Celle-ci est fausse: une référence doit toujours être initialisée.
4. char t[10];
5. q = p;
Celle-ci est correcte: on affecte la valeur d'un pointeur au travers
duquel on peut modifier la mémoire à un pointeur au travers duquel on
ne peut pas le faire, mais ça ne pose pas de problème particulier
(i.e. les types sont préservés).
6. r = p;
La ligne 6 est fausse: on ne peut affecter un pointeur à un
caractère. Quelques-uns ont indiqués que la ligne était fausse parce
que la déclaration de r était fausse, une réponse acceptée.
7. char &s = *p;
8. p = &s;
9. s = p;
Faux: on ne peut affecter un pointeur à un caractère.
10. p = s;
Faux: on ne peut davantage affecter un caractère à un pointeur.
11. s = *p;
12. *p = s;
13. t = p;
Faux: un pointeur qui dénote le premier élément d'un tableau ne peut
être modifié.
14. p = t;
15. *t = *p;
Cette dernière ligne est correcte, elle correspond à l'affectation de
la première case du tableau (puisque le nom d'un tableau est un
pointeur qui dénote l'adresse du tableau, c'est à dire de la première
case.