IN104 Programmation orientée objet

publicité
IN104
Programmation orientée objet – Introduction aux objets
Séance de Travaux Dirigés du 27 novembre 2008
B. Monsuez, F. Védrine
Exercice 1 – Notre compteur en C++
Nous étudions dans cet exercice l’exemple du compteur présenté dans le cours.
Vous trouverez dans le répertoire suivant ~bmonsuez/Pub/IN204/TD1/C++ un groupe de projets que vous
recopierez dans un répertoire local.
Quelques références pour C++
Je conseille personnelle le Stanley B. Lippman « L’essentiel du C++ » ou « A C++ Primer » en anglais
qui est assez clair comme première introduction tout en étant assez complet.
Sur la page du cours, d’autres livres sont mis avec en commentaires leurs forces et faiblesses ainsi que des
livres téléchargeables.
Question n° 0 : Lancer cbuilder et charger dans l’environnement de cbuilder le groupe de projet td1.bpgr.
Compiler le projet.
Question n° 1 : Le projet ne compile pas et affiche une erreur relative à ce qu’un champ n’est pas
accessible. Expliquer pourquoi cette erreur se produit et corriger là.
Question n° 2 : Le projet compile et s’exécute, cependant la valeur des champs est différente dans le cas
où les objets sont alloués sur la pile où sont alloués sur le tas.
En vous reportant au cours, essayez de donner une explication à cette différence de fonctionnement.
Proposer une solution pour initialiser par défaut les compteurs.
Question n° 3 : Dans la fonction useObjectA(), le programme initialise les différents champs des
compteurs. Ceci n’est pas très élégant.
Proposer une solution pour créer des compteurs en passant en paramètre le nombre maximal d’éléments
comptés à partir duquel le compteur revient à 0.
Simplifier le code de la fonction useObjectA() une fois que vous avez proposé ces solutions.
Question n° 4 : Proposer enfin un constructeur de recopie qui permet de créer un nouveau compteur étant
la copie d’un compteur passé en paramètre.
Question n° 5 : Nous souhaitons que lorsque nous détruisons un compteur celui-ci signale sa destruction
en affichant un message sur la console ainsi que sa valeur au moment de la destruction.
Exercice 2 – Notre compteur en Java
Vous trouverez dans le répertoire suivant ~bmonsuez/Pub/IN204/TD1/Java un groupe de fichiers que
vous recopierez dans un répertoire local.
Quelques références pour java
Pour la documentation sur java, les documents de Sun concepteur de JAVA sont disponibles en ligne.
Un bon tutoriel pour java est disponible à cette adresse :
http://java.sun.com/docs/books/tutorial/index.html
L’intégralité des fonctions disponible pour Java est disponible à l’adresse suivante :
http://java.sun.com/j2se/1.5.0/docs/api/
Une référence complète du langage qui correspond au livre normatif est disponible à cette adresse :
http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html
Quelques différences entre la structure d’un programme en Java et en C++
Les unités de compilation
Les programmes C++ et les programmes Java suivent à peu près le même schéma d’implantation. Ils sont
généralement répartis sur plusieurs fichiers appelés « unités de compilation » permettant de définir des
« modules ».
Une unité de compilation comporte d’une part les définitions des fonctions et structures de données ainsi
que le code permettant d’implanter ces fonctions et structure de données.
Pour les programmes C++, les unités de compilation ont une extension .cc ou .cpp (extension prise pour
les TDs), et pour les programmes Java, les unités de compilation ont une extension .java.
Cependant, ils existent des différences notables entre les deux approches. En C++, une unité de
compilation peut contenir la définition de plusieurs classes. En Java, à chaque classe correspond une unité
de compilation qui porte le nom de la classe. Ainsi le fichier MyClass.java correspond à l’unité de
compilation contenant uniquement la définition de la classe MyClass.
Plusieurs classes peuvent faire parties d’un même package (espace de nom). Chaque package peut inclure
des sous-packages, ces derniers sont séparés par un « . ».
Pour accéder à la définition d’une classe définie dans une unité de compilation C++, il suffit de faire
include <unite.h > si le fichier unite.h contient la définition de la classe.
Pour java, il est possible d’inclure des unités la classe en utilisant l’instruction import suivi du nom du
package et du nom de la classe.
import java.lang.String
importe toutes la classe String qui a été définie dans le pakage java.lang.
Il est cependant possible d’importer toutes les classes définies dans un package. Ainsi l’instruction :
import java.lang.*
va importer toutes les classes appartenant au package java.lang.
La fonction main
Dans un programme C++, il est impératif de définir une fonction main ayant cette forme :
int main(int argc, char* argv[])
{
...
return error code ;
}
Dans un programme JAVA, une classe de démarrage doit être défini. Cette classe de
démarage doit comporter une méthode main
public class Main
{
public static void main(String[] argv)
{ ...
}
}
La compilation en java
Pour compiler un fichier *.java en un fichier *.class, il suffit de faire
javac fichier.java
Pour compiler un projet, il faut compiler chacun des fichier *.java en *.class.
Enfin pour lancer un programme java, il faut faire :
java nom de la classe
Question n° 1 Récupérer le code dans répertoire en question, compiler ce code et exécuter le ?
Comparer le code et expliquer les différences avec le code en C++ précédent.
Question n° 2 Nous voulons ajouter un constructeur spécialisé pour créer un compteur Proposer une
solution pour créer des compteurs en passant en paramètre le nombre maximal d’éléments comptés à
partir duquel le compteur revient à 0.
Ecrivez cette méthode et traduisez la fonction du programme C++ useObjectA() dans le programme Java.
Question n° 3 Nous souhaitons avoir un mécanisme générique correspond au constructeur par recopie en
Java. Proposer un tel mécanisme.
Question n° 4 : Nous souhaitons que lorsque qu’un compteur est détruit, il affichant un message sur la
console ainsi que sa valeur au moment de la destruction.
Un programme comporte historiquement des structures de données et des algorithmes, ces derniers
fonctionnant sur le support fournis par les structures de données. Les unités de compilation Java traitent
simultanément des algorithmes et des structures de données, alors que les programmes C++ placent
naturellement les structures de données dans des fichiers .h ou fichiers d’en-têtes partagés entre plusieurs
unités de compilation, les algorithmes étant naturellement répartis sur les différentes unités de
compilation.
Exercice 3 – Écriture de classes C++ et Java
Cette partie vise à définir des classes pour des objets graphiques. La plupart des bibliothèques d’interfaces
graphiques sont écrites dans des langages orientés objets, qui sont particulièrement adaptés pour. C’est
notamment le cas de Eclipse (Java – IBM), Swing (Java – Sun), CBuilderX (Borland - Inprise), MFC
(Microsoft), Qt (Trolltech). Dans cette partie, nous définissons les classes de points, de segment et de
droite dans le plan.
S1
P4
P2
P1
P3
Question n° 1 En vous inspirant du code fourni, écrivez, compilez et déboguez soit dans les langages
C++ et Java
• La classe Point, comprenant une abscisse entière et une ordonnée entière et donnant un accès à ces
champs.
• La classe Segment constituée de deux extrémités sous la forme de Point. Un segment est construit
à l’aide de deux points et a une méthode pour savoir si un point est dans le segment ou non.
Donnez une méthode indiquant le type d’intersection de deux segments et donnez une autre
méthode retournant cette intersection en fonction du type.
• La classe Droite constituée d’un Point et d’un vecteur indiquant la direction, vecteur également
constitué d’une partie abscisse et d’une partie ordonnée. Définissez un constructeur à partir d’un
segment, et un constructeur à partir d’un point et d’une direction. Donnez une méthode indiquant
le type d’intersection de deux segments et donnez une autre méthode retournant cette intersection
en fonction du type. Repérez qu’à la différence de C, toutes les méthodes d’intersection
s’appellent intersect et il est inutile de préciser le type des arguments dans le nom de la méthode.
Question n° 2 : Ajouter à toutes ces classes des méthodes d’affichage pour le déboguage.
Question n° 3 : A la classe droite
Ajouter une méthode rotate90() effectuant une rotation de 90 degrés à une droite.
Ajouter une méthode move(int x, int y) effectuant une translation à une droite.
Question n° 4 : Écrivez le programme suivant en C++ et écrivez son homologue en Java.
#include <iostream>
int main() {
Droite d1(Point(0,0), Point(4, 2));
Droite d2(d1);
d1.print();
std::cout << std::endl;
d2.print();
std::cout << std::endl;
d1.rotate90();
d1.print();
std::cout << std::endl;
d2.print();
return 0;
}
Est ce qu’il aurait été possible d’utiliser l’opérateur new en C++ aussi ? Ecrivez la nouvelle fonction en
utilisation l’allocation sur le tas.
Exercice 4 – Présentation avancée des classes C++ et Java (Hors
TD)
Une classe est introduite comme une extension de structure de donnée (le type struct en C), extension qui
contient une algorithmique locale. L’algorithmique locale est l’algorithmique directe liée à une instance
de classe (= un objet de la classe). Elle s’oppose à l’algorithmique globale qui est quant à elle répartie sur
plusieurs instances de classes.
À titre d’exemple, nous présentons une classe un peu complexe, avec un objet partagé de type
SharedNode entre plusieurs instances de la classe MyObject. Cette classe est définie dans le langage C++
et dans le langage Java. L’objet partagé a pour but de garder cohérents entre elles les différents objets.
Pour cela il a un mécanisme de notification qui peut être appelé à partir de n’importe quel objet
enregistré. Les questions ne requièrent pas une bonne compréhension de ce mécanisme et ont simplement
pour de reconnaître les différents constituants d’une classe, avant de comprendre à quoi elle peut servir.
SharedNode
notification
fields
MyObject
value
value
value
value
value
namespace ENSTA {
package ENSTA;
class SharedNode;
public class SharedNode;
class MyObject {
private:
int value;
/* fields */
char secondaryFields;
Node* sharedNode;
public class MyObject {
private int value;
private char secondaryFields;
private Node sharedNode;
bool simplify(); // private method
public:
// Constructor with default field initializer
MyObject(int
valueSource=0)
:
value(valueSource),
secondaryFields(0), sharedNode(NULL) {}
// Copy constructor
MyObject(const MyObject& source)
: value(source.value), secondaryFields(source.secondaryFields),
sharedNode(source.sharedNode)
{ if (sharedNode) sharedNode->addCaller(*this); }
// Assign operator
MyObject& operator=(const MyObject& source)
{ if (sharedNode && this != &source)
sharedNode->removeCaller(*this);
sharedNode = NULL;
value = source.value;
secondaryFields = source.secondaryFields;
sharedNode = source.sharedNode;
if (sharedNode && this != &source)
sharedNode->addCaller(*this);
return *this;
}
// Destructor
~MyObject()
{ if (sharedNode)
sharedNode->removeCaller(*this);
}
// Simple get and set field access method to complete construction
void incValue() { ++value; }
void decValue() { --value; }
int getValue() const { return value; }
void setValue(int valueSource) { value = valueSource; }
// Elaborate public use methods
void clear()
{ value = 0;
public MyObject(int valueSource=0)
{ value = valueSource; secondaryFields = 0; sharedNode = null; }
public MyObject(MyObject source)
{ value = source.value;
secondaryFields = source.secondaryFields;
sharedNode = source.sharedNode;
if (sharedNode != null)
sharedNode.addCaller(this);
}
public Object clone() { return new MyObject(this); }
public MyObject assign(MyObject source)
{ if (sharedNode && this != source)
sharedNode.removeCaller(this);
sharedNode = null;
value = source.value;
secondaryFields = source.secondaryFields;
sharedNode = source.sharedNode;
if (sharedNode && this != source)
sharedNode.addCaller(this);
return this;
}
public ~MyObject()
{ if (sharedNode)
sharedNode.removeCaller(this);
}
public void incValue() { ++value; }
public void decValue() { --value; }
public int getValue() { return value; }
public void setValue(int valueSource) { value = valueSource; }
public void clear()
{ value = 0;
secondaryFields = 0;
if (sharedNode)
sharedNode.removeCaller(this);
sharedNode = null;
}
public void mergeWith(MyObject source)
secondaryFields = 0;
if (sharedNode)
sharedNode->removeCaller(*this);
sharedNode = NULL;
}
void mergeWith(const MyObject& source)
{ assert(sharedNode == source.sharedNode);
value = (value > source.value) ? value : source.value;
secondaryFields |= source.secondaryFields;
simplify();
}
void intersectWith(const MyObject& source)
{ assert(sharedNode == source.sharedNode);
value = (value < source.value) ? value : source.value;
secondaryFields &= source.secondaryFields;
simplify();
}
void notifyAll(const SharedNode::Function& function);
throws java.lang.Exception
{ if (sharedNode != source.sharedNode)
throw new java.lang.Exception();
value = (value > source.value) ? value : source.value;
secondaryFields |= source.secondaryFields;
simplify();
}
public void intersectWith(MyObject source)
{ if (sharedNode != source.sharedNode)
throw new java.lang.Exception();
value = (value < source.value) ? value : source.value;
secondaryFields &= source.secondaryFields;
simplify();
}
private boolean simplify();
};
};
} // end of namespace ENSTA
Question n° 1 :
• Repérez les différences de syntaxe entre les deux langages
• Repérez la déclaration des classes
• Identifiez les champs, les méthodes, la partie construction, complément à la construction,
utilisation, destruction.
Téléchargement