PROFDINFO.COM

Votre enseignant d'informatique en ligne

Laboratoire 4 - Pointeurs, allocation dynamique et références

Exercice 4.1 - Le drill

Étant donné le programme suivant:

int main()
{
   int x=1; 
   int y=3; 
   int z, *px, *py; 
   px= &x; 
   *px *= 2; 
   py= &y; 
   z= 4*( *py - *px);
}

En supposant que les adresses des variables x, y, z, px et py sont respectivement 0x0012fba0, 0x0012fb94, 0x0012fb88, 0x0012fb7c et 0x0012fb70:

  1. Quelle est la valeur de px ?
  2. Quelle est la valeur de *px ?
  3. Quelle est la valeur de &*px ?
  4. Quelle est la valeur de py ?
  5. Quelle est la valeur de *py ?
  6. Quelle est la valeur de z ?
  7. Quelle est la valeur de *&z?

N.B.: Utilisez Visual Studio seulement pour valider vos réponses!

Exercice 4.2 - Pointeurs et fonctions

Étant donné le programme suivant:

void FonctionA(int x)
{
   x=25;
}
void FonctionB(int *x)
{
   x=0;
}
void FonctionC(int *x)
{
   *x=10;
}

int main()
{
   int i=5;
   FonctionA(i);
   FonctionB(&i);
   FonctionC(&i);
}

Déterminez la valeur de la variable i après chaque appel de fonction. Utilisez Visual Studio pour valider vos réponses! Pouvez-vous expliquer ces résultats?

Exercice 4.3 - Le jeu des erreurs

Pour chacun des programmes suivants, identifiez l'erreur de compilation ou de fonctionnement:

  1. int main()
    {
       float a;
       int *p = &a;
    }

  2. int main()
    {
       int *p; 
       (*p)++;
    }

  3. int main()
    {
       int i=3; 
       int *p = i;
       *p = 4;
    }

  4. int main()
    {
       int i=3; 
       int *p = &i;
       *i = 4;
    }

  5. int main()
    {
       int i=3; 
       int *p = 0x0012fba0;
    }

  6. int* Adresse()
    {
       int i=0;
       return &i;
    }
    int main()
    {
       int *p = Adresse();
       (*p)++;
    }

  7. void Unite(int* p)
    {
       p=1;
    }
    int main()
    {
       int i = 3;
       Unite(&i);
    }

  8. void Zero(int* x)
    {
       *x=0;
    }
    int main()
    {
       int i;
       Zero(i);
    }

Exercice 4.4 - La fonction Echanger

Programmez la fonction Echanger qui permet d'échanger le contenu de deux variables. La fonction Echanger doit avoir le prototype suivant:


void Echanger(int *n1, int *n2);

Testez le bon fonctionnement de votre fonction avec un programme principal qui fait un appel à la fonction Echanger.

Exercice 4.5 - Entretien avec un vampire (un pointeur d'objet)

Complétez le programme suivant de façon à créer un vampire dans le tas avec le nom Lestat, passer le vampire à la fonction Activer, appeler la méthode Mordre et détruire le vampire.


#include <iostream>  Lestat  
#include <string>
using namespace std;

class CVampire
{
   string Nom_;
public: 
   CVampire(string nom);
   void Mordre();
};

CVampire::CVampire(string nom) : Nom_(nom)
{
}

void CVampire::Mordre()
{
   cout << Nom_ << " te mord, ayoye." << endl;
}

void Activer(CVampire *pVampire)
{
   // 3. Appel de la méthode Mordre du vampire.
}

int main()
{
   // 1. Création d'un vampire dans le tas avec le nom
   //    Lestat.  Il faut utiliser l'opérateur new.

   // 2. Appel de la fonction activer avec le vampire.

   // 4. Destruction du vampire.
   //    Il faut utiliser l'opérateur delete.

}

Exercice 4.6 - Entretien avec plusieurs vampires (un tableau de pointeurs)

Complétez le programme suivant de façon à créer trois vampires dans le tas avec les nom Lestat, Louis et Claudia. Ensuite, passer le tableau à la fonction Activer, appeler la méthode Mordre pour chacun des vampires et, finalement, détruire tous les vampires.


const int NBVAMPIRES = 3;vampire
void Activer(CVampire* Vampires[])
{
   // 3. Appel de la méthode Mordre pour tous les vampires.


}
int main()
{
   CVampire* Tableau[NBVAMPIRES];
   // 1. Création d'un vampire dans le tas pour chaque 
   //    case du tableau. Les noms des vampires sont
   //    Lestat, Louis et Claudia.  Il faut utiliser 
   //    l'opérateur new.
 
 

   // 2. Appel de la fonction activer avec le tableau.


   // 4. Destruction de tous les vampires.
   //    Il faut utiliser l'opérateur delete.


}

Exercice 4.7 - Entretien avec plusieurs vampires (un tableau dynamique)

Complétez le programme suivant de façon à demander à l'usager le nombre de vampires à créer. Il faudra ensuite allouer un tableau dynamique de la bonne taille, passer le tableau à la fonction Activer, appeler la méthode Mordre pour chacun des vampires et, finalement, détruire tous les vampires.

void Activer(CVampire Vampires[], int Taille)
{
   // 5. Appel de la méthode Mordre pour tous les vampires
 
 
}
int main()
{
   // 1. Déclarer une variable pour conserver le nombre de vampires


   // 2. Demander le nombre de vampires à l'usager


   // 3. Allouer un tableau dynamique de vampires
   //    Vous faudra-t-il ajouter un constructeur à la classe CVampire?


   // 4. Appel de la fonction Activer avec le tableau


   // 6. Destruction du tableau de vampires

}

Exercice 4.8 - L'argent tombe du ciel!

Copiez votre exercice 3.7 dans un nouveau répertoire et renommez-le Exercice 4.8. Ajoutez à votre projet la classe CSou qui doit dériver de la classe CPersonnage et qui contient les membres suivants:

Ensuite, complétez le programme principal suivant:

#include <SFML/Graphics.hpp>
#include "Exercice4.8.h"
#include "Sou.h"
using namespace sf;

/////////////////////////////////////////////////////////////////////////////
// InitialiserSous
//
// La fonction initialise un tableau de pointeurs de CSou
// en allouant dynamiquement un nouveau sou pour chaque
// case du tableau. La position initiale du sou est déterminée
// alléatoirement.
// Intrants:
//   Tableau de pointeurs de CSou
//   Taille du tableau
//   Référence à une texture à utiliser pour les sous
/////////////////////////////////////////////////////////////////////////////
void InitialiserSous(CSou* Tableau[], int Taille, Texture& SouImg)
{

}

/////////////////////////////////////////////////////////////////////////////
// DeplacerSous
//
// Pour chaque case du tableau, la fonction vérifie si le pointeur 
// n'est pas zéro. Si le pointeur n'est pas zéro, le sou est déplacé 
// vers le bas. Si le déplacement est impossible, le sou doit être détruit 
// et le pointeur doit être mis à zéro.
// Intrants:
//   Tableau de pointeurs de CSou
//   Taille du tableau
/////////////////////////////////////////////////////////////////////////////
void DeplacerSous(CSou* Tableau[], int Taille)
{

}

/////////////////////////////////////////////////////////////////////////////
// EncoreDesSous
//
// La fonction vérifie s'il y a au moins encore un sou dans le tableau.
// Pour ce faire, elle vérifie si au moins une case n'est pas zéro.
// Intrants:
//   Tableau de pointeurs de CSou
//   Taille du tableau
// Extrant: Vrai s'il y a encore au moins un sou dans le tableau,
//          faux s'il n'y a plus aucun sou dans le tableau.
/////////////////////////////////////////////////////////////////////////////
bool EncoreDesSous(CSou* Tableau[], int Taille)
{

}

/////////////////////////////////////////////////////////////////////////////
// AfficherSous
//
// Pour chaque case du tableau, la fonction vérifie si le pointeur 
// n'est pas zéro. Si le pointeur n'est pas zéro, le sou est affiché 
// dans la fenêtre.
// Intrants:
//   Tableau de pointeurs de CSou
//   Taille du tableau
//   Fenetre
/////////////////////////////////////////////////////////////////////////////
void AfficherSous(CSou* Tableau[], int Taille, RenderWindow& Fenetre)
{

}

// Main fourni
int main(int argc, char *argv[])
{
    const int NBSOUS = 100;
    CSou* Sous[NBSOUS];
    Texture FondImg;
    Texture SouImg;
    SouImg.loadFromFile("sou.png");
   
    srand(static_cast<int>(time(0)));  // Pour initialiser le random

    RenderWindow Fenetre(VideoMode(Hauteur, Largeur),"L'argent tombe du ciel!");
    if (!FondImg.loadFromFile("fondmario.bmp"))
    {
        return EXIT_FAILURE;
    }
    Sprite Fond(FondImg); // Créer un sprite avec la texture du fond

    InitialiserSous(Sous,NBSOUS, SouImg);

    while(EncoreDesSous(Sous,NBSOUS))
    {
        Fenetre.clear();
        Fenetre.draw(Fond);
        AfficherSous(Sous, NBSOUS, Fenetre);
        DeplacerSous(Sous,NBSOUS);
        Fenetre.display();
        sleep(milliseconds(5));
    }

    return 0;
}

pouletExercice 4.9 - Engraisser un poulet

Voici un petit programme qui permet d'engraisser un poulet.

  1. Identifiez les lignes qui ne compilent pas, expliquez pourquoi ces lignes ne compilent pas, puis mettez en commentaires ces lignes de façon à ce que le programme compile sans erreurs.
  2. Déterminez la grosseur finale du poulet.
#include <iostream>
using namespace std;

class CPoulet
{
   int Grosseur_;
public:
   CPoulet() : Grosseur_(0) {}

   int GetGrosseur() const 
   { 
      return Grosseur_; 
   }
   int& GetGrosseur2()  
   { 
      return Grosseur_; 
   }
   const int& GetGrosseur3() const 
   { 
      return Grosseur_; 
   }
   void SetGrosseur(int Grosseur) 
   { 
      Grosseur++; 
      Grosseur_ = Grosseur;
   }
   void SetGrosseur2(int& Grosseur)
   { 
      Grosseur++; 
      Grosseur_ = Grosseur;
   }
   void SetGrosseur3(const int& Grosseur) 
   { 
      Grosseur++; 
      Grosseur_ = Grosseur;
   }
};

void Test1(CPoulet Poulet)
{
   Poulet.GetGrosseur()  = Poulet.GetGrosseur() +1;
   Poulet.GetGrosseur2() = Poulet.GetGrosseur2()+1;
   Poulet.GetGrosseur3() = Poulet.GetGrosseur3()+1;
   Poulet.SetGrosseur (Poulet.GetGrosseur() +1);
   Poulet.SetGrosseur2(Poulet.GetGrosseur2()+1);
   Poulet.SetGrosseur3(Poulet.GetGrosseur3()+1);
}

void Test2(CPoulet& Poulet)
{
   Poulet.GetGrosseur()  = Poulet.GetGrosseur() +1;
   Poulet.GetGrosseur2() = Poulet.GetGrosseur2()+1;
   Poulet.GetGrosseur3() = Poulet.GetGrosseur3()+1;
   Poulet.SetGrosseur (Poulet.GetGrosseur() +1);
   Poulet.SetGrosseur2(Poulet.GetGrosseur2()+1);
   Poulet.SetGrosseur3(Poulet.GetGrosseur3()+1);
}

void Test3(const CPoulet& Poulet)
{
   Poulet.GetGrosseur()  = Poulet.GetGrosseur() +1;
   Poulet.GetGrosseur2() = Poulet.GetGrosseur2()+1;
   Poulet.GetGrosseur3() = Poulet.GetGrosseur3()+1;
   Poulet.SetGrosseur( Poulet.GetGrosseur() +1);
   Poulet.SetGrosseur2(Poulet.GetGrosseur2()+1);
   Poulet.SetGrosseur3(Poulet.GetGrosseur3()+1);
}

int main()
{
   CPoulet Poulet1;
   CPoulet& Poulet2 = Poulet1;
   const CPoulet& Poulet3 = Poulet1;
   Test1(Poulet1);
   Test2(Poulet1);
   Test3(Poulet1);
   Test1(Poulet2);
   Test2(Poulet2);
   Test3(Poulet2);
   Test1(Poulet3);
   Test2(Poulet3);
   Test3(Poulet3);
}

lapinlapinlapin Exercice 4.10 - Chauds lapins

Copiez votre exercice 3.7 dans un nouveau répertoire et renommez-le Exercice 4.10. Ajoutez à votre projet la classe CLapin qui doit dériver de la classe CPersonnage et qui a la déclaration suivante:

class CLapin : public CPersonnage
{
	// Liste des genres possibles
	enum Genre { Male, Femelle };

	// Genre du lapin
	Genre Genre_;

	// Âge du lapin entre 0 et 100
	int Age_;

	// Mutateur privé
	void SetAge(int Age);

public:
	// Constructeur paramétrique
	CLapin(Texture& LaTexture, float x, float y);

	// La méthode Reproduire prend en paramètre une référence à un autre
	// lapin, ainsi qu'une référence à une texture à utiliser pour l'éventuel
	// enfant. La méthode doit vérifier si le lapin courant se reproduit 
	// avec le lapin passé en paramètre.
	// Les lapins se reproduisent s'il ne sont pas du même genre, si les
	// deux lapins ont plus de 30 ans et si leurs différences 
	// de position en x et y sont les deux moins de 16 pixels.
	// Si les lapins se reproduisent, un nouveau lapin et créé dans le tas
	// avec la position d'un parent et le pointeur et retourné à l'appelant.
	// Si les lapins ne se reproduisent pas, un pointeur 0 est retourné.
	CLapin* Reproduire(const CLapin& Lapin, Texture& LaTexture);

	// La méthode Déplacer choisit une direction au hasard (haut, bas, gauche
	// droite) et déplace le lapin de 8 pixels dans cette direction. 
	// De plus, elle incrémente l'age du lapin d'un an.
	void Deplacer();

	// Accesseurs
	Genre GetGenre() const { return Genre_; }
	int GetAge() const { return Age_; }
};

La classe CTerrier permet de gérer jusqu'à 300 lapins et a la déclaration suivante:

// Nombre maximum de lapins
const int MAXLAPINS = 300;

// Nombre de lapins au départ de la simulation
const int NBLAPINSINITIAL = 15;

class CTerrier
{
	// Tableau de pointeurs de lapins
	CLapin* Tab_[MAXLAPINS];

	// La méthode PlacerLapin recoit un pointeur sur un nouveau lapin
	// et doit placer ce lapin dans le tableau.
	// Pour savoir à quelle position du tableau le lapin doit être
	// placé, le méthode doit chercher la première place disponible, 
	// c'est-à-dire dont le pointeur vaut zéro.
	void PlacerLapin(CLapin* Lapin);

public:
	// Constructeur paramétrique. Il doit créer les 15 premiers lapins
	// à des positions aléatoires et initialiser le reste du tableau 
	// avec des zéros.  La référence à la texture sera utilisée pour les 
	// lapins.
	CTerrier(Texture& LaTexture);

	// Cette méthode doit vérifier tous les lapins dans le tableau.
	// Si un lapin est âgé de 100 ans ou plus, il doit être détruit
	// et le pointeur correspondant doit être remis à zéro. 
	void NettoyageLapins();

	// La méthode AfficherLapins doit demander à tous les lapins du
	// tableau de s'afficher dans la fenêtre.
	void AfficherLapins(RenderWindow& Fenetre);

	// La méthode ReproduireLapins vérifie toutes les reproductions
	// possibles. Pour ce faire, chaque lapin du tableau doit être
	// testé avec tous les autres lapins du tableau autre que lui-même.
	// Pour tester si deux lapins se reproduisent, on appelle la méthode
	// Reproduire de premier lapin en passant le deuxième lapin (ainsi que
	// la texture à utiliser pour l'enfant éventuel) en paramètre. 
	// Si la méthode Reproduire du lapin retourne un pointeur non nul, 
	// il faut alors utiliser la méthode PlacerLapin pour placer ce nouveau
	// bébé lapin dans le tableau.
	void ReproduireLapins(Texture& LaTexture);

	// La méthode DeplacerLapins doit demander à tous les lapins du
	// tableau de se déplacer. La méthode doit retourner TRUE s'il n'y 
	// a plus de lapin dans le tableau ou si le nombre de lapins 
	// est égal au maximum de lapins permis. Dans tous les autres
	// cas, la méthode retourne FALSE.
	bool DeplacerLapins();



	// Le destructeur doit détruire les lapins restants dans le tableau.
	~CTerrier();
};


De plus, il vous faut utiliser le programme principal suivant:

#include "Exercice4.10.h"
#include "Terrier.h"
#include <SFML/Graphics.hpp>
#include <ctime> // Pour le time dans le srand

int main(int argc, char *argv[])
{
	srand(static_cast<int>(time(0)));
	bool Fin = false;
	RenderWindow Fenetre(VideoMode(Largeur, Hauteur),"Le terrier");
	Texture FondImg;
	if (!FondImg.loadFromFile("prairies.bmp"))
	{
		return EXIT_FAILURE;
	}
	Sprite Fond(FondImg);

	Texture LapinImg;
	if (!LapinImg.loadFromFile("lapin.png"))
	{
		return EXIT_FAILURE;
	}

	CTerrier UnTerrier(LapinImg);
	int tours = 0;

	while(!Fin)
	{
		Fenetre.clear();
		Fenetre.draw(Fond);
		UnTerrier.AfficherLapins(Fenetre);
		Fenetre.display();
		sleep(milliseconds(100));
		UnTerrier.NettoyageLapins();
		UnTerrier.ReproduireLapins(LapinImg);
		Fin = UnTerrier.DeplacerLapins();
	}
	return 0;
}

Pour réaliser votre exercice, vous pouvez utiliser cette image de lapin lapin et cette image de prairies:

prairies

Exercice 4.11 - Pointeurs et références

Complétez le programme suivant:

 

#include "unobjet.h"
void FaireUneChosePointee(CUnObjet * pChose);
void FaireUneChoseParReference(CUnObjet & objet);

int main()
{
	CUnObjet Quelconque;
	... ;                             // (1) déclaration de pTruc 

	pTruc = new CUnObjet;
	Quelconque = ... ;                // (2) affecter à Quelconque le contenu de l’objet pointé par pTruc

	FaireUneChosePointee( ... );      // (3) passer Quelconque à la fonction FaireUneChosePointee
	FaireUneChoseParReference( ... ); // (4) passer Quelconque à la fonction FaireUneChoseParReference
	FaireUneChoseParReference( ... ); // (5) passer l’objet pointé par pTruc à FaireUneChoseParReference

	...;                              // (6) instruction(s) manquante(s) ?
	return 0;
}
    

Exercice 4.12 - Classes, pointeurs et références

Soit le programme suivant:

 

#include <iostream>
using namespace std;

//----------------------------------------------
class C
{
	int i_;
public:
	C(int i) : i_(i) { }
	int get () const { return i_; }
	void set(int i) { i_= i; }
};

//----------------------------------------------
void FaireR (C & o)
{
	o.set(o.get()+5);
}

//----------------------------------------------
void FaireP (C * p)
{
	p->set(p->get()+10);
}

//----------------------------------------------
void FaireCreation (C * p)
{
	p = new C(202);
}
 
//----------------------------------------------
int main()
{
	C* pObjet 		= 0;
	C  oObjet(101);
	C& rObjet   	= oObjet;
	C* pNouveau 	= 0;

	pObjet = new C(14);
	FaireR (*pObjet);
	cout << pObjet->get() << endl;

	FaireP (&oObjet);
	cout << oObjet.get() << endl;
	cout << rObjet.get() << endl;

	FaireP (pObjet);
	cout << pObjet->get() << endl;

	FaireR (rObjet);
	cout << oObjet.get() << endl;

	FaireCreation (pNouveau);
	cout << pNouveau->get() << endl;

	delete pObjet;
}

Quel sera le résultat de l'exécution de ce programme?

Exercice 4.13 - Héritage, pointeurs et trinité

Déterminez la sortie du programme suivant :

 

class A 
{
   string s_; 
public:
   A(string s) : s_(s) { cout << s_; }
   ~A() { cout << "b"; }
};

class B : public A
{
   string t_;
public:
   B() : A("1"), t_("2") { cout << t_; }
   ~B() { cout << "d"; }
};

class C
{
   B* b_;
public:
   C() { b_ = new B(); cout << "e"; }
   void Afficher() const { cout << "f"; }

   // Trinité
   C(const C& c) { b_ = new B(); }
   C& operator=(const C& c) { delete b_; b_ = new B(); return *this; }
   ~C() { delete b_; }
};

void testp(const C* p)
{
   p->Afficher();
}

void testo(const C o)
{
   o.Afficher();
}

int main()
{
   C c1;
   C c2;
   testp(&c1);
   c2=c1;
   testo(c2);
}

Réponse: