| Home | Analyses | C++ | Java | Internet | Pattern | Securité | |
J'ai diffusé des petits problèmes C++ dans le forum fr.comp.lang.c++. Vous trouverez ici les énoncés des différents problèmes diffusés à ce jour. Les solutions se trouvent sur le forum ou dans mon livre.
| Questions | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
| 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
| 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
short add(short i, short j)
{ return i+j; }
Le compilateur peut annoncer un warning, pourquoi ?
class CString
{ char* pt;
public:
CString(const char* src)
{ pt=new char[strlen(src)+1];
strcpy(pt,src);
}
~CString()
{ delete [] pt;
}
};
CString f()
{ CString str("test");
return str;
}
void main()
{ CString x=f();
}
C'est incorrect, pourquoi ?
class CString
{ char* pt;
public:
CString()
{ pt=(char*)::malloc(4);
::strcpy(pt,"ABC");
cout << "CString::CString()" << endl;
}
~CString()
{ ::free(pt);
cout << "CString::~CString()" << endl;
}
};
void main()
{ CString* pt=new CString[3];
// ...
delete pt;
}
Qu'affiche main, pourquoi et comment corriger ?
void main()
{
char* pt;
pt=new char [10];
// ...
delete pt;
}
C'est incorrect, pourquoi ? (N.B.: char n'est pas un objet !)
class CFigure
{ public:
virtual void trace()
{ cout << "CFigure::trace()" << endl;
}
~CFigure()
{ cout << "CFigure::~CFigure()" << endl;
}
};
class CCercle : public CFigure
{ public:
virtual void trace()
{ cout << "CCercle::trace()" << endl;
}
~CCercle()
{ cout << "CCercle::~CCercle()" << endl;
}
};
void main()
{
CFigure* p;
p=new CFigure();
p->trace();
delete p;
p=new CCercle();
p->trace();
delete p;
}
Qu'affiche main, pourquoi et comment corriger ?
class CHopital
{ public:
void afficheNom()
{ cout << "CHopital::afficheNom()" << endl;
}
};
void main()
{
void (*ptf)(void); // Pointeur de fonction
ptf=&CHopital::afficheNom;
ptf(); // Appel de CHopital::f()
}
Ce n'est pas compilable, pourquoi et comment corriger ?
class CHomme
{ int age;
public:
virtual void trace() const
{ cout << "CHomme::trace()" << endl;
}
virtual ~CHomme()
{}
};
class CDocteur : public CHomme
{ const char* diplome;
public:
virtual void trace() const
{ cout << "CDocteur::trace()" << endl;
}
};
void f(const CHomme& x)
{ x.trace();
}
void g(const CHomme x)
{ x.trace();
}
void main()
{ CDocteur docteur;
f(docteur);
g(docteur);
}
Qu'affiche main, pourquoi ?
template <class T>
class A
{ public:
bool b(const T*) { return true; }
bool b(const void*) { return false; }
};
template <class T1,class T2>
inline bool B(const T1&,const T2& t2)
{
return (A<T1>().b(&t2));
}
Que fait la fonction B ?
class CString
{ char* buf;
public:
CString(const char* str)
{ buf=(char*)::malloc(::strlen(str)+1);
::strcpy(buf,str);
}
CString(const CString& x)
{ buf=(char*)::malloc(::strlen(x.buf)+1);
::strcpy(buf,x.buf);
}
~CString()
{ ::free(buf);
}
CString& operator =(const CString& x)
{ ::free(buf);
buf=(char*)::malloc(::strlen(x.buf)+1);
::strcpy(buf,x.buf);
return *this;
}
};
Cette classe présente un risque d'erreur potentiel à l'utilisation. Dans quel cas et
comment corriger ?
template <class T> class A
{ T m;
public:
A(const T z)
{ m=z; }
};
Ce template peut échouer, pour quel type d'objet ?
class CBorne
{ int maximum;
int minimum;
public:
CBorne(int t) : minimum(t), maximum(minimum +1) {}
void print() { cout << minimum << ',' << maximum << endl; }
};
void main()
{ CBorne borne=3;
borne.print();
}
Qu'affiche main, pourquoi ?
class A
{ public:
int a;
};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
void main()
{
cout << "offset de a dans D " << offsetof(D,a) << endl;
}
La macro offsetof de la norme Ansi C permet d'obtenir l'offset d'un attribut dans une structure. Ce
n'est pas exécutable, pourquoi ?
class CFigure
{ public:
virtual void trace()
{ cout << "CFigure::trace()" << endl;
}
// Constructeur
CFigure()
{ trace();
}
virtual ~CFigure()
{}
};
class CCercle : public CFigure
{ public:
virtual void trace()
{ cout << "CCercle::trace()" << endl;
}
};
void main()
{
CFigure figure;
CCercle cercle;
cercle.trace();
}
Qu'affiche main, pourquoi ?
CString& f()
{ CString a="ab";
return a;
}
CString& g()
{ CString a="/dir/";
CString b="file.ext";
return a+b;
}
void main()
{ cout << f() << endl;
cout << g() << endl; // 1
}
Qu'affiche main, pourquoi et comment corriger ?
void main()
{
char* pt;
pt=new char [10];
// ...
pt=(char*)realloc(pt,sizeof(*pt)*20);
// ...
}
C’est incorrect, pourquoi ?
template <class T>
class A
{ public:
void f(int x);
void f(T x);
};
Cela ne fonctionne pas dans tous les cas. Pourquoi ?
class CFichierTemp
{ FILE* st;
char nom[255];
public:
CFichierTemp()
{ ::strcpy(nom,"abc.tmp");
st=::fopen(nom,"w");
}
~CFichierTemp()
{ ::fclose(st);
::unlink(nom);
}
};
void f()
{ int a;
// ...
exit(0);
}
void main()
{ CFichierTemp tmp;
f();
}
Il y a une erreur, laquelle ? Comment corriger ?
class CFigure
{ public:
CFigure()
{ cout << "CFigure::CFigure()" << endl;
}
~CFigure()
{ cout << "CFigure::~CFigure()" << endl;
}
};
static jmp_buf env;
void f()
{ CFigure figure;
longjmp(env,1);
}
void main()
{
if (!setjmp(env))
{ f();
}
else
{ // ...
}
}
Qu’affiche main ?
class CRefEntier
{ public:
int& ri;
CRefEntier(int& i) : ri(i) {}
CRefEntier& operator =(const CRefEntier& x)
{ if (this!=&x)
{ ri=x.ri; }
return *this;
}
};
void main()
{ int i=1;
int j=2;
CRefEntier i1(i);
CRefEntier i2(j);
i1=i2;
cout << "i=" << i << ",j=" << j << endl;
}
Qu’affiche main ?
class CCompteur1
{ public:
int cnt1;
CCompteur1()
{ cnt1=100;
}
void dec()
{ --cnt1;
}
};
CCompteur1 GlobalCompteur1;
Fichier 2 :
extern CCompteur1 GlobalCompteur1;
class CCompteur2
{ public:
int cnt2;
CCompteur2()
{ GlobalCompteur1.dec();
cnt2=GlobalCompteur1.cnt1;
}
};
CCompteur2 GlobalCompteur2;
void main()
{ cout << "cnt1=" << GlobalCompteur1.cnt1 << endl;
cout << "cnt2=" << GlobalCompteur2.cnt2 << endl;
}
Qu’affiche main et pourquoi ?
class CString
{ char buf[10];
public:
CString(const char* x)
{ strcpy(buf,x); }
operator const char* () const
{ return buf; }
};
const char* Globalpt=CString("ABC");
void main()
{ cout << Globalpt << endl;
}
Qu’affiche main, pourquoi ?
void main()
{
int i=5;
cout << i + 3;
cout << i & 3; // Erreur
}
Ce n'est pas compilable, pourquoi et comment corriger ?
void f(char& r)
{ r=3; }
void main()
{ long l=0;
f((char&)l);
cout << l << endl;
}
Qu'affiche main ?
class CFigure
{ public:
virtual void trace() =0;
// Constructeur
CFigure()
{ trace();
}
virtual ~CFigure()
{}
};
class CCercle : public CFigure
{ public:
virtual void trace()
{ cout << "CCercle::trace()" << endl;
}
};
void main()
{
CCercle cercle;
cercle.trace();
}
Qu’affiche main et pourquoi ?
void f(long) { cout << "f(long)" << endl; }
void f(char*) { cout << "f(char*)" << endl; }
void main()
{ f(3);
f(NULL);
}
Qu'affiche main, pourquoi ?
class CApPhoto;
class CObjectif
{ public:
operator CApPhoto() const;
};
class CApPhoto
{ CObjectif _objectif;
public:
CApPhoto(const class CObjectif& objectif)
: _objectif(objectif) {}
};
inline CObjectif::operator CApPhoto() const
{ return CApPhoto(*this); }
void f(const CApPhoto& apphoto)
{ cout << "f" << endl;
}
void main()
{ CObjectif objectif;
f(objectif); // Erreur
}
Ce n’est pas compilable, pourquoi ?
class CObjNom
{ public:
void* operator new(size_t,const char*);
};
void main()
{ CObjNom* p;
p=new ("main") CObjNom();
p=new CObjNom(); // Erreur
}
Ce n’est pas compilable, pourquoi ?
class CCompte;
class CFenetreCompte;
class CClient
{ CCompte* _compte;
public:
CClient(CCompte* compte) : _compte(compte)
{ }
void affiche();
};
inline CClient* createClient(CFenetreCompte* fenCpt)
{ return new CClient((CCompte*)fenCpt); } //1
class CFenetre
{ int _taille;
//...
};
class CCompte
{ int _solde;
public:
virtual int solde() const
{ cout << "CCompte::solde" << endl;
return _solde;
}
virtual ~CCompte()
{}
};
class CFenetreCompte : public CFenetre,public CCompte
{
};
void CClient::affiche()
{ _compte->solde();
}
void main()
{ CFenetreCompte c;
CClient* client=createClient(&c);
client->affiche();
}
Ce n’est pas exécutable, pourquoi ?
class CEntier
{ int _n;
public:
CEntier()
{ cout << "CEntier::CEntier()" << endl;
}
CEntier(int n)
{ _n=n;
cout << "CEntier::CEntier(int)" << endl;
}
void operator=(int n)
{ _n=n;
cout << "CEntier::operator=(int)" << endl;
}
};
class CDeuxEntier
{ CEntier _a;
int _b;
public:
CDeuxEntier()
{ _a=4;
_b=5;
}
};
void main()
{ CDeuxEntier c;
}
Qu’affiche main, pourquoi ?
class CEntier
{ int i;
public:
CEntier(int z=0)
{ i=z; }
int operator ++()
{ i++;
return i;
}
};
void main()
{ CEntier a;
a++; // Erreur
}
Ce n'est pas compilable, pourquoi ?
class CObj
{ public:
CObj()
{ cout << "CObj::CObj()" << endl; }
CObj(const CObj&)
{ cout << "CObj::CObj(const CObj&)" << endl; }
};
CObj f() { return CObj(); }
CObj g()
{ CObj rc=f();
return rc;
}
L'appel d'un constructeur de copie peut être évité, comment ?
class CEntier
{ int a;
public:
CEntier()
{ cout << "CEntier::CEntier()" << endl; }
CEntier& operator =(const CEntier&)
{ cout << "CEntier::operator =()" << endl;
return *this;
}
CEntier& operator =(int x)
{ a=x;
return *this;
}
};
void main()
{ CEntier a1;
a1=1;
// ...
CEntier a2;
a2=a1;
}
Qu'affiche main, pourquoi ?
class xMsg
{ public:
xMsg() {}
xMsg(const xMsg&)
{ cout << "xMsg::xMsg(const xMsg&)" << endl; }
virtual void print() const
{ cout << "print" << endl; }
virtual ~xMsg()
{}
};
void f()
{ throw xMsg();
}
void main()
{
try
{ f();
}
catch(xMsg x)
{ x.print();
}
}
Qu’affiche main, pourquoi ?
class CComplex
{ protected:
float reel;
float imma;
public:
CComplex();
CComplex(float xreel,float ximma);
CComplex(const CComplex& x);
// ...
CComplex& operator =(const CComplex& x);
CComplex operator +(const CComplex& x);
CComplex& operator +=(const CComplex& x);
};
CComplex CComplex::operator +(const CComplex& x)
{ return CComplex(reel+x.reel,imma+x.imma);
}
CComplex& CComplex::operator +=(const CComplex& x)
{ *this=*this+x;
return *this;
}
Les opérateurs + et += ne sont pas optimisés, comment les
améliorer ?
class CVide
{
};
void main()
{ cout << sizeof(CVide) << endl;
cout << sizeof('A') << endl;
}
Qu’affiche main ?
void main()
{
for (int i=0;i<10;++i)
;
if (i==10) cout << "Fin de boucle" << endl;
}
Ce n'est pas compilable, pourquoi ?
class CEntier
{ public:
int i;
CEntier(int z)
: i(z) {}
};
class CRefEntier
{ CEntier& ra;
public:
CRefEntier(CEntier& x)
: ra(x)
{ cout << "i=" << ra.i << endl;
}
};
class CEntierRefEntier : public CRefEntier
{ CEntier a;
public:
CEntierRefEntier()
: a(3), CRefEntier(a)
{}
};
void main()
{ CEntierRefEntier c;
}
Qu'affiche main, pourquoi ?
class CEntier
{ int a;
public:
CEntier(int x) { a=x; }
int valeur() { return a; }
};
void fn(const CEntier& a)
{ cout << a.valeur() << endl; // Erreur
}
void main()
{ CEntier a=3;
fn(a);
}
Ce programme ne se compile pas, pourquoi ?
class CEntier
{ protected:
int x;
};
class CEntierEtendu : public CEntier
{
void f()
{ CEntier* p=this;
x=5; // Ok
p->x=3; // Erreur
}
};
L'accès à x via p dans la fonction
CEntierEtendu::f() est impossible. Pourquoi et comment corriger ?
class CFigure
{ int a;
};
class CCercle : public CFigure
{ int b;
};
class CCarre : private CFigure
{ int c;
};
void main()
{ CCercle cercle;
CCarre carre;
CFigure* pFigure;
pFigure=&cercle;
pFigure=&carre; // Erreur
}
Ce n’est pas compilable, pourquoi ? Comment corriger ?
struct A { int mya; };
struct B : A { int myb; };
struct C : virtual A { int myc; };
struct D { int myd; };
struct E : D, A { int mye; };
void main()
{
B b;
C c;
E e;
A* ptA;
ptA=&b;
cout << ((void*)ptA==(void*)&b) << endl;
ptA=&c;
cout << ((void*)ptA==(void*)&c) << endl;
ptA=&e;
cout << ((void*)ptA==(void*)&e) << endl;
}
Qu'affiche main ?
class CList
{ const CList& next;
public:
CList(const CList& x)
: next(x) {}
};
Comment créer uniquement la première instance de CList ?
class CEntier
{ public:
CEntier(int) {}
};
void f(CEntier& x)
{ // ...
}
void main()
{ f(3); // Erreur
}
Ce n’est pas compilable, pourquoi ?
class CStatic
{ public:
CStatic() { cout << "CStatic::CStatic()" << endl; }
};
void f()
{ cout << "f()" << endl;
static CStatic StaticA;
}
void main()
{ cout << "main()" << endl;
f();
f();
}
Qu’affiche main, pourquoi ?
int z; int& a=z; int& const b=z;Une référence ne peut être initialisée qu’à la construction. Elle se comporte donc comme une constante. L’opérateur égal d’une référence, modifie l’objet référencé, mais pas la référence elle-même. b est une référence constante et non une référence sur une constante. Quelle est la différence entre a et b ?
class CUnEntier
{ public:
int a;
CUnEntier() : a(0) { }
};
class CDeuxEntiers : public CUnEntier
{ public:
int b;
CDeuxEntiers() : b(1) { }
};
void f(CUnEntier* tab1)
{ tab1[1].a=2;
}
void main()
{ CDeuxEntiers tab2[2];
f(tab2);
cout << tab2[0].a << endl;
cout << tab2[0].b << endl;
cout << tab2[1].a << endl;
cout << tab2[1].b << endl;
}
Qu’affiche main, pourquoi ?
class CEntier
{ int i;
public:
CEntier() : i(0) {}
void print(ofstream& o)
{ o << "CEntier=" << i; }
};
Cette écriture limite l’utilisation de la classe. Pourquoi ?
lock pour le même handle ne renvoient pas le même pointeur
mémoire mais toujours le même contenu. Cette approche peut également être
utilisée pour augmenter artificiellement la mémoire disponible en utilisant un
fichier temporaire. Lorsque le programme appelle un lock, le tampon correspondant est
chargé depuis le fichier vers la mémoire. Lors d’un unlock, le
tampon est recopié dans le fichier. L’inconvénient de ce type d’approche
est que le programme doit scrupuleusement suivre les locks et les unlocks
afin de ne pas en oublier en chemin. La classe indiquée dans l’exemple suivant permet
de s’affranchir de cela. La puissance du C++ permet de simuler un pointeur et de bloquer la
mémoire utilisable lorsque cela est nécessaire.
// Gros objet à mettre dans un fichier temporaire ou ailleurs
class CBigObjet
{ public:
char buf[10000];
CBigObjet(char xFill) { ::memset(buf,xFill,sizeof(buf)); }
};
// Fonctions ::GetHandle, ::Lock et ::Unlock
typedef int HANDLE;
// ...
// Classe de simulation de pointeur sur CBigObjet
class CPtBigObjet
{ static HANDLE lockHandle; // Dernier handle bloqué
HANDLE handle;
CBigObjet* lock() const
{ ::Unlock(lockHandle);
return (CBigObjet*) ::Lock(lockHandle=handle);
}
public:
CPtBigObjet(HANDLE h) : handle(h) { }
CPtBigObjet(const CPtBigObjet& h) : handle(h.handle) { }
CBigObjet& operator *() const { return *lock(); }
CBigObjet& operator [](int i) const { return lock()[i]; }
CBigObjet* operator ->() const { return lock(); }
// ...
};
HANDLE CPtBigObjet::lockHandle=NULLHandle;
void main()
{ // Initialise via un handle
CPtBigObjet pta=::GetHandle(sizeof(CBigObjet));
CPtBigObjet ptb=::GetHandle(sizeof(CBigObjet));
*pta=CBigObjet('A');
ptb[0]=CBigObjet('B');
pta->buf[1]='C';
pta->buf[0]=ptb->buf[0];
}
Les syntaxes d'utilisations du pointeur dans main montrent que l’utilisation de
l’objet est similaire à un pointeur. Pourtant, il y une erreur. Laquelle et comment la
corriger ?
class A
{ public:
virtual void f();
virtual ~A()
{}
};
class B : public A
{ public:
virtual void f();
};
void B::f()
{ A::f(); // Appel de la classe de base
// ...
}
Il serait parfois confortable de pouvoir appeler la méthode de la classe de base et de
l’indiquer par un mot clef (du type super). Comment faire ?
class CVehicule {};
class CVoiture : public CVehicule {};
class CAvion : public CVehicule {};
Si vous manipulez un pointeur ou une référence de type CVehicule et que vous
désirez une copie de l'objet référencé, vous ne savez pas si vous devez
appeler le constructeur de copie de CVoiture ou de CAvion.
void duplique(const CVehicule& vehicule)
{ CVehicule* dup=new CVoiture((const CVoiture&)vehicule); // ???
ou new CAvion((const CAvion&)vehicule); // ???
//...
delete dup;
}
Comment faire ?
f qui
accepte une écriture comme ceci.
f(f); // Recoit un pointeur de fonction
Une modification mineure de la classe peut entraîner une cascade de recompilation, car cette modification peut avoir des conséquences sur un ensemble de classes. Comment éviter ces recompilation en cascade ?
Quand et pourquoi utiliser une référence ?
EOF qui
indique un caractère incorrect, est stockée dans un entier et non dans un char. En
effet, toutes les valeurs possibles d’un char sont valides. Il a fallu trouver une valeur
supplémentaire à mettre dans un type de taille supérieure à char. La
fonction fgetc() retourne un entier et non un char pour pouvoir vérifier la
valeur EOF. Un template voulant retourner une valeur d’erreur, doit connaître les deux
types possibles à manipuler : le type de base, et le type permettant de retourner une
erreur. Comment déclarer un template du type « pile » dont la méthode
pop() retourne l’objet paramètre, ou une valeur d’erreur lorsque la
pile est vide ? Une pile de caractère retournera le caractère présent dans
celle-ci ou EOF si la pile est vide. Une pile d'autres objets doit pouvoir retourner
également un code d'erreur.