[C++] Machine à boissons

Résolu/Fermé
Utilisateur anonyme - 6 avril 2008 à 19:56
mamiemando Messages postés 33545 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 25 février 2025 - 11 avril 2008 à 10:14
Bonjour,

J'aurais à développer un mini-projet en C++ d'une machine à boissons et ceci me fait peur car c'est la première fois que je mets mes connaissances (qui sont pas trop fortes) en pratique. D'habitude, je code des bouts de programmes.

Voilà mon diagramme de classe :
https://www.cjoint.com/?egtHS4a8RX

J'ai converti toute classe en UML en une autre en C++ et créé les fichiers .h et .cpp (ça a donné 9 fichiers en total).

Je serais reconnaissante si quelqu'un jette un coup d'oeil sur mes fichiers et me donne ses remarques afin que j'avance.

Merci de votre temps.

achat.h :
#include "achat.h"

achat::achat(int som)
{
	somme = som;
}

achat::~achat(){};

achat.cpp :

#include "achat.h"
achat::achat(int som)
{
	somme = som;
}

achat::~achat(){};

boisson.h :
#include<iostream>
#include<string>

#if !defined(__ObjectOrientedModel_1_boisson_h)
#define __ObjectOrientedModel_1_boisson_h


class boisson
{
private:
   string nom;
   int prix;
   int quantite;

public:
   void selectionner(string); 
   void recuperer(int); 
   void inserer(int); 
   boisson(string,int,int);
   ~boisson();

};

#endif

boisson.cpp :
#include "boisson.h"
using namespace std;

void boisson::selectionner()
{
   char c;
		cout<<"1.Coca Cola"<<endl;
		cout<<"2.Fanta"<<endl;
		cout<<"3.Boga"<<endl;
		cout<<"4.Apla"<<endl;
		cout<<"0.Quitter"<<endl;
		cout<<"Faites votre choix"<<endl;

		cin>>c;
		if(c==1)
		{
			cout<<"La boisson a servir est Coca Cola"<<endl;
			
		}
		else
			if(c==2)
			{
				cout<<"La boisson a servir est Fanta"<<endl;
				
			}
			else
				if(c==3)
				{
					cout<<"La boisson a servir est Boga"<<endl;
					
				}
				else 
					if(c==4)
						{
							cout<<"La boisson a servir est Apla"<<endl;
						}
					else
						if(c==0)
							{	
								cout<<"Vous avez choisi de quitter le programme."<<endl;
							}
						else 
							{
								cout<<"Vous devriez faire votre choix a partir de la liste des boissons disponibles"<<endl;
							}
				
}


void boisson::recuperer(int qte_stock)
{
   if (qte_stock >= 1)
	   qte_stock --;
}


void boisson::inserer(int qte_stock)
{
   if(qte_stock==0)
	   switch (nom_boisson)
			case 1 : "Coca Cola";
				qte_stock++;
			case 2 : "Fanta";
				qte_stock++;
			case 3 : "Boga";
				qte_stock++;
			case 4 : "Apla";
			    qte_stock++;
			default : cout<<"Vous ne pouvez pas stocker autre type de boisson !"<<endl;


}


boisson::boisson(string nom_boisson, int prix_boisson, int quantite_boisson)
{
	nom=nom_boisson;
	prix=prix_boisson;
	quantite=quantite_boisson;
}


boisson::~boisson(){};

caisse.h :
#include<iostream>
#include<string>

#if !defined(__ObjectOrientedModel_1_caisse_h)
#define __ObjectOrientedModel_1_caisse_h

class piece;

class caisse
{
public:
   void recharger(int);
   void recuperer(int);
   caisse();
   ~caisse();

   piece** piece;

};

#endif

caisse.cpp :
#include "piece.h"
#include "caisse.h"


void caisse::recharger(int qte_stock)
{
   piece_100 = new piece;
   piece_500 = new piece;
   piece_1000 = new piece;
   
}


void caisse::recuperer(int qte_stock)
{
   // TODO : implement
}


caisse::caisse()
{
   piece = NULL;
}


caisse::~caisse(){}; 


Au niveau du fichier caisse.cpp, je ne sais pas comment instancier les objets (des pièdes de monnaies de différentes valeurs) ; un héritage est possible dans ce cas ?

:)
A voir également:

9 réponses

Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
10 avril 2008 à 22:39
Bonjour, bonjour,

Le fil tourne autour de quelques points techniques comme l'héritage, qu'est-ce qu'une interface etc.

Le sujet principal est la structure du projet, quelles classes et quelles méthodes pour le projet.


A vrai dire je pense qu'il y a deux façons de voir le projet:

1) Simuler un distributeur.
C'est le cas où il faut je pense donner une représentation à chaque objet d'un distributeur réel : chaque boisson, chaque pièce doit exister. C'est une reconstitution de la réalité via un programme.

2) Implémenter un distributeur.
Ici, c'est le cas où on ne cherche pas à imiter mais simplement à réaliser un programme qui ressemblerait à celui qui existe dans un distributeur.

Pour moi le cas 1) se prête plus à un projet à faire avec un langage objet qui va donner lieu à une évaluation. (Déjà comme ça j'ai un peu peur que la prof fasse un tilt sur les FILE *, on sait jamais : /) Le cas 2) est le programme que je ferais en pratique car personne ne va vraiment se soucier du fait que l'on a fait des classes réutilisables et autres futilités. Les pièces sont des float, les boissons des char * et on compte le nombre de xchaque plutôt que de les représenter séparément, en somme : Vive le C et Zou !

Quelle tendance donneriez-vous au projet ?
Je pense que cela dépend beaucoup du contexte aussi, est-ce dans le cadre d'un cours d'initiation à l'informatique ou plutôt de programmation orientée objet ? Le tout est de savoir quelle part de C on peut se permettre par rapport au C++.

M.

PS:
Très belle explication de Mamiemando. ;-)
Ca manquait un peu de Pourquoi dans la mienne.
2
Utilisateur anonyme
10 avril 2008 à 22:43
Bonsoir,
J'étais là quand tu dinais ;)
Au fait, il s'agit d'une simulation d'un distributeur dans le cadre d'un cours de programmation orientée objet...
-1
Utilisateur anonyme
9 avril 2008 à 15:25
Pour instancier la classe Piece, je devrais avoir recours à ces deux alternatives :

- Soit je surcharge des constructeurs en modifiant à chaque fois la valeur de la pièce :
Piece::Piece_100(int valeur_piece,int quantite_piece)
{
valeur_piece = 100;
quantite=quantite_piece;
}

Piece::Piece_500(int valeur_piece,int quantite_piece)
{
valeur_piece = 500;
quantite=quantite_piece;
}

Piece::Piece_1000(int valeur_piece,int quantite_piece)
{
valeur_piece = 1000;
quantite=quantite_piece;
}


- Soit j'utilise un patron de conception et le code serait ainsi :
void Piece::FabriquePiece() 
{
	FabriquePiece::GetInstance()->NouvellePiece(100);
    FabriquePiece::GetInstance()->NouvellePiece(500);
    FabriquePiece::GetInstance()->NouvellePiece(1000);
}


Donc pas d'héritage ni de classe template...

le projet utilise un paquetage Distributeur contenant une classe distributeur, une interface Piece et une interface Boisson.
--> Quand tu évoques le mot "interface" ici, tu veux dire un attribut ? La classe Distributeur aura des attributs et des méthodes (constructeur et destructeur éventuellement) ?
Si nous parlons de classe Distributeur, donc nous devrions omettre le main ? C'est ça ?

Le corps de la classe Distributeur serait :
distributeur.h :
#ifndef _DISTRIBUTEUR_H
#define _DISTRIBUTEUR_H

#include<iostream>
#include<string>

class Distributeur
{
public :
	Distributeur();
	~Distributeur();
    void Inserer(Boisson ); //elle aura comme paramètre un pointeur sur Boisson ?

private :

	//les attributs privés

};


La classe template n'est pas une surcharge d'opérateurs par hasard, car d'après ton exemple elle ressemble à une surcharge de l'opérateur +.

La méthode selectionner de la classe Boisson -comme je t'ai dit sert à sélectionner une boisson à partir de la liste des choix disponibles, comme une classe Distributeur a vu le jour, donc on pourrait pas en faire partie ? On déplace la méthode selectionner à la classe Distributeur.

Regarde la méthode selectionner dans le fichier boisson.cpp :
#include "boisson.h"
using namespace std;

void Boisson::selectionner( const Boisson *pBoisson ) const; 
{
   char c;
		cout<<"1.Coca Cola"
		"2.Fanta"
		"3.Boga"
		"4.Apla"
		"0.Quitter"
		"Faites votre choix"<<endl;

		cin>>c;
		if(c==1)
		{
			cout<<"La boisson a servir est Coca Cola"<<endl; /* je ne pourrais pas ici pointer le nom de la boisson que le client a choisi au lieu d'écrire en lettres le nom de la boisson ? */
			
		}
		else
			if(c==2)
			{
				cout<<"La boisson a servir est Fanta"<<endl;
				
			}
			else
				if(c==3)
				{
					cout<<"La boisson a servir est Boga"<<endl;
					
				}
				else 
					if(c==4)
						{
							cout<<"La boisson a servir est Apla"<<endl;
						}
					else
						if(c==0)
							{	
								cout<<"Vous avez choisi de quitter le programme."<<endl;
							}
						else 
							{
								cout<<"Vous devriez faire votre choix a partir de la liste des boissons disponibles"<<endl;
							}
				
}


La méthode inserer de la classe Boisson est là pour que le technicien insère une quantité de boissons (Coca, Apla, ou autre) dans le stock s'il est vide. Elle est dans le mauvais endroit ? J'avoue, elle est mal-écrite, alors je vais essayer de la réécrire.

A plus tard :)
1
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
9 avril 2008 à 20:32
C'est encore moi.

Pour la classe Piece:
A vrai dire le patron la factory risque d'utiliser elle aussi une Piece avec un héritage. (Pas obligé mais pour faire plaisir à ta prof...)
La différence est que la classe qui va créer des pièces via une factory ne connaît pas les vraies classes. Juste une interface (je reviens sur le terme après)
Encore un peu de vocabulaire, la "surcharge" d'une méthode c'est la définition d'une méthode du même nom mais avec des arguments différents.
Exemple:
void afficher( int i );
void afficher( float f );
void afficher( char *sz );

L'interface est un concept de la programmation objet, on la retrouve explicitement en Java, en C++ on utilise le terme lorsque l'on code une classe qui revient au même qu'une interface mais cela reste du vocabulaire tandis qu'en Java (ou autre) il y a un mot clef. (justement "interface Machin" à la place de "class Machin")
Une interface c'est une classe qui n'a aucun attribut, seulement des fonctions qui sont toutes virtuelles pures.

exemple:

class IBoisson
{
public:

   // Constructeur/destructeur normaux et vides.
   IBoisson   {}
   virtual ~IBoisson()   {}

   // Des méthodes.

   virtual const char *GetName() const = 0;
   ...
};


Le fait d'avoir au moins une méthode virtuelle pure fait qu'aucune instance de l'interface (/la classe) ne peut être créée. Il faut obligatoirement une classe héritée. Cela sert principalement à faire une abstraction, poser une idée en quelque sorte. On dit, voilà, j'ai des boissons, cela pourra être des sodas des cocas, des jus fruits etc, mais elles auront toutes une méthode pour connaître le nom par exemple. Ainsi, on peut faire un programme qui gère des IBoisson. Si quelqu'un veut rajouter des limonades il fait une classe qui respecte l'interface IBoisson (via un héritage et l'implémentation des méthodes nécessaires) et le programme marchera avec ses limonades puisqu'une limonade est une IBoisson.

Globalement c'est ce qui va se passer si tu fais une factory. Elle créera des IBoissons, le programme utiliseras des IBoisson de manière uniforme sauf qu'il pourra exister des boissons différentes.

Une interface n'a pas forcement de .cpp. Elle en aura un si elle définit un corps pour ses méthodes virtuelles pures ou éventuellement si on lui a mis des méthodes statiques.


Je ne vois pas de lien entre la classe distributeur et le main. Ni même entre le paquetage Distributeur et le main. Le main a à voir avec le paquetage de l'interface graphique à la limite... c'est tout.


La classe template n'est pas une surcharge d'opérateurs par hasard, car d'après ton exemple elle ressemble à une surcharge de l'opérateur +.

Ici c'était une fonction template. En effet cela ressemble à une surcharge, sauf que je n'ai pas eu besoin de ré-écrire la fonction un coup avec des int, un coup avec des float etc. On l'écrit une fois et c'est bon. (C'est là toute la puissance) Ensuite on peut spécialiser une template, c'est à dire ré-écrire la fonction pour dire pour tel type tu feras tel comportement spécifique plutôt que celui général.
A la rigueur ici, à coups de copier-coller on peut s'en sortir sans le template. Si la méthode peut prendre deux paramètres de types différents cela commence à faire beaucoup de copier-coller pour définir tous les cas. En template il suffit de mettre un deuxième paramètre template et c'est bon.
Pour les classes template il faut parfois donner explicitement le type quand le compilateur ne peut pas le déduire. par exemple:

list<IBoisson *> lesBoissons;
list<IPiece *> lesPieces;

C'est comme si on venait d'écrire une liste chaînée de IBoisson et une autre de IPiece. Là où c'est bien aussi c'est que celui qui a écrit la classe list ne connaissait même pas les IBoissons et les IPiece. Et ça marche. On a une liste chaînée qui prend en paramètre des IBoisson * et en renvoie en valeur de retour.
En passant on remarque l'intérêt de l'interface IBoisson. On peut ici gérer un ensemble de boissons sans se soucier de leur type réel.

Pour reprendre l'exemple des pièces, on aurait pu écrire une classe template sur la valeur des pièces. Ensuite, plutôt que de refaire de .h et .cpp et de ré-écrire toutes les classes on fait:
Piece<100>;
Piece<500>;
Piece<1000>;

Et on vient d'écrire trois classes. (Des vrais types différents donc)


Pour en revenir à sélectionner et insérer, je ne les mettrais pas sur la IBoisson.

Quel est ton choix pour cette classe d'ailleurs, représente-t-elle une boisson ou un type de boisson avec sa quantité dans le distributeur etc ?


Pour après ;-).

M.
1
mamiemando Messages postés 33545 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 25 février 2025 7 829
10 avril 2008 à 21:06
Message de khaoula
La classe Piece a deux méthodes qui sont :
- recharger (pour dire ajouter des pièces à la caisse, c'est le manutentionnaire qui s'en occupe)
- recuperer (pour que le manutentionnaire récupère de l'argent de la caisse, en fin de mois par exemple)
Mais je me demande si ces deux méthodes feront partie de la classe caisse ou bien elles devront appartenir à la classe Piece.


Classe caisse sans hésiter. La question c'est
1- la pièce est ajoutée dans la caisse => méthode de pièce, this non modifié, paramètre modifié : caisse
inline bool est_valeur_piece_valide(const double & x){
  return x == 0.10 || x == 0.20 || x == 0.50 || x == 1 || x ==2;
}

class piece_t{
  protected:
    double valeur;

  public:

    piece_t(const double & v):valeur(v){
      if(!est_valeur_piece_valide(v)) throw;
    }

    inline const double & get_valeur() const{
      return valeur;
    }


    // Mauvaise idée :-(
    inline void ajouter_dans caisse(caisse_t & c) const{
      c.ajouter_piece(*this); // mmh la je suis cuit :-s
    }
};

2- la caisse engrange une pièce => méthode de caisse, this modifié , paramètre constant : pièce

class caisse_t{
  protected:
    std::map<double,unsigned> map_valeur_nb; // mappe pour chaque valeur de pièce le nombre de pièce stockée
  public:
    caisse_t(){}

    // Cette fonction est inévitable ;-)
    inline void ajouter_piece(const piece_t & p){
      const double & val = p.get_valeur();
      ++map_valeur_nb[valeur];
    }
};

En terme de design de classe il n'y a donc pas a hésiter, car la méthode en gras (1) va de toute façon nécessiter d'ajouter dans la classe caisse une méthode en italique (2)

D'un point de vue plus pratique il n'est même pas nécessaire de définir un objet pièce (sauf pour le fun), d'ailleurs ma map stocke directement la valeur (ce qui m'évite d'avoir à définir un opérateur < sur les pièces pour créer ma map).

En soit instancier une pièce n'a pas d'intérêt, seule sa valeur (si elle est valide) est importante. Ainsi on peut complètement dégager la classe piece et faire à la place :
#include <map>

inline bool est_valeur_piece_valide(const double & x){
  return x == 0.10 || x == 0.20 || x == 0.50 || x == 1 || x ==2;
}

class caisse_t{
  protected:
    std::map<double,unsigned> map_valeur_nb; // mappe pour chaque valeur de pièce le nombre de pièce stockée
  public:

    caisse_t(){}

    // Cette fonction est inévitable ;-)
    inline bool ajouter_piece(const double & p){
      if(est_valeur_piece_valide(p)) return false; // pièce invalide
      ++map_valeur_nb[valeur];
      return true; // pièce valide insérée avec succès
    }
};

Bon maintenant dernière ligne droite. Une caisse n'est qu'une map. Pour virer une pièce il suffit donc que je vérifie qu'au moins une pièce de cette valeur est en caisse (il suffit d'utiliser la méthode find). Si c'est le cas je vérifie qu'il y a bien au moins une et je décrémente...

Bonne chance
1

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
6 avril 2008 à 23:51
Salutations,

Je regarderai un peu plus le code demain soir.

J'ai un peu peur de mal te conseiller. Je pense que créer un instance pour chaque pièce peut alourdir le programme étant donné que c'est un objet plutôt courant. Ceci dit, si il y a un prof de C++ dans le coin on essaiera de faire objet pour lui faire plaisir même si c'est moins pratique. Apparemment il y a un prof dans le coin...

Il y a une chose que l'on fait très généralement, c'est que une instance doit être détruite par l'objet qui l'a créée. C'est beaucoup plus facile à maintenir et à retrouver si un objet est bien détruit quelque part dans le code.
Dans un tel programme les pièces ou les boissons se promènent partout. Je conseillerai d'avoir une classe pour gérer les pièces et une pour les boissons. Une classe qui soit accessible d'un peu partout et qui permettent de créer une nouvelle instance, de la détruire, éventuellement de lister les instances courantes et surtout, de faire le nettoyage complet si elle même est détruite. (bien souvent à la fin du programme) Evidemment si je dis qu'une classe du genre est plutôt courante... Wikipédia: la Factory. (/Home parfois Fabrique en français) Elle a aussi un autre but qui est de créer des objets dont la classe réelle n'est pas connue en dehors de la factory.
Je te laisse la curiosité d'explorer les autres patrons de conceptions. (Design patterns) Pour ce projet seul la fabrique et le Singleton (utilisé dans la fabrique) seront utiles (Le singleton assure qu'une seule instance d'une classe ne peut-être créée)

Les boissons, tu as choisis de les implémenter en tant qu'une classe représentant le type de boisson et qui contient un nombre de boissons de ce type. Les pièces qui ont un peu la même importance dans le programme sont elles crées instance par instance. Toutes les solutions sont acceptables mais j'aurais harmonisé les deux.

Je pense qu'il manque un élément fondamental selon moi à ta conception: Une classe Distributeur, non ?
Ce serait la classe principale avec l'interface Piece et l'interface Boisson.

Pour ta question concernant la création des pièces je pense que tu as déjà mon point de vue.

quelque chose du genre FabriquePiece::GetInstance()->NouvellePiece( 10 ); qui crée une pièce d'une valeur de 10 euros ou échoue si la factory ne sait pas le faire. La caisse n'a plus qu'à stoquer des pointeurs. (dans un std::vector<Piece> ?) Deuxième partie, pour représenter une pièce je ferais un héritage et une classe template (on oublie si tu ne connais pas du tout) Idem pour les boissons selon moi. (sans template cette fois)
Petite question à côté: Le distributeur rend-t-il la monnaie ?

Ce ne sont que des propositions, je suis ouvert à tout. Il faut voir aussi avec ce que l'on vous a appris. Toujours pareil... faire plaisir au prof...

En cas de soucis technique surtout pas n'hésite pas. (Et aussi si j'ai été totalement incompréhensible ^^")
M.
-1
Utilisateur anonyme
8 avril 2008 à 23:36
Bonsoir,

Merci de tes explications :)

Je pense qu'il manque un élément fondamental selon moi à ta conception: Une classe Distributeur, non ?
--> Pour la classe Distributeur, tu as raison, apparemment je vais devoir l'ajouter. Je pensais qu'elle n'est que l'association de la classe Stock et la classe Caisse. Ce qui me chiffonne ce sont ses attributs et ses méthodes, car j'ai "presque" tout prévu dans les autres classes. Dans le diagramme de classe, elle serait composée (le losange blanc) des deux classes Stock et Caisse.

Les pièces qui ont un peu la même importance dans le programme sont elles crées instance par instance.
--> Pour la classe Piece, d'après ce que j'ai compris, des héritages (selon la valeur de la pièce) seront envisageables afin d'instancier les objets. J'aurais recours à cette syntaxe :
classe cent_millimes : public piece
{
//implémentation des méthodes
}

Tu as proposé ça :
FabriquePiece::GetInstance()->NouvellePiece(10);


J'utiliserais les patrons de conception comme ça au niveau de la classe Piece ?
FabriquePiece::GetInstance()->NouvellePiece(100);

FabriquePiece::GetInstance()->NouvellePiece(500);

FabriquePiece::GetInstance()->NouvellePiece(1000);


pour représenter une pièce je ferais un héritage et une classe template (on oublie si tu ne connais pas du tout)</ital>
--> Pour la classe template, je n'ai jamais rencontré.

Petite question à côté: Le distributeur rend-t-il la monnaie ?
Le distributeur rend la monnaie normalement, oui :)
-1
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125 > Utilisateur anonyme
9 avril 2008 à 01:15
Bonsoir, ^^

1).
La classe distributeur... en effet c'est une classe qui ne fait pas grand chose. Le projet distributeur ne peut normalement pas être figé, je veux dire par là un exécutable qui met trois coca, un soda et achète deux cocas. A la rigueur cela pourrait être un exécutable avec une interface pour faire des choix et rendre utilisable à souhait le distributeur. Mais c'est dommage, on a un distributeur, mais il restera collé derrière une interface, moi justement je voulais faire un distributeur avec une pure interface graphique 3D. Je ne pourrai pas.
Tout ça pour dire que ton projet pourrait parfaitement être une brique ré-utilisable par n'importe qui. En somme, une bibliothèque plutôt qu'un exécutable. Et là, plus de problème, tout le monde peut le ré-utiliser. Pour moi même si le distributeur reste collé à une interface console (on ne va quand même pas faire ce qui n'est pas demandé, sauf si tu as envie ;-) Ca ne serait pas vraiment long), il est important que ton distributeur soit bien détaché de l'interface graphique. En bref, le rôle de la classe distributeur est d'être l'interface de ton paquetage Distributeur. Elle n'a l'air de rien mais pour faire un programme qui utilise un distributeur de boisson j'aimerais bien tomber sur Une classe. J'en crée une instance et j'ai quasiment tout qui ce qu'il faut. Sinon, c'est commencer par créer une caisse, créer un stock, non, trop compliqué. Il faut vraiment voir le distributeur et l'interface graphique comme deux parties dont l'une est dépendante de l'autre. En UML tu peux facilement schématiser les paquetages. Du point de vue interface graphique, le projet utilise un paquetage Distributeur contenant une classe distributeur, une interface Piece et une interface Boisson.
On reprécise la chose, il y a un troisième paquetage que je vois se dessiner. Une sorte de paquetage "Environnement global" qui contient les fabriques pour les pièces et les boissons. (on pourrait faire deux paquetages séparés en théorie mais bon...) Maintenant l'interface graphique utilise Les fabriques pour créer des pièces et détruire les boissons achetées. Elle utilise toujours le distributeur qui lui utilise aussi le paquetage des fabriques. Déjà cela signifie que ton distributeur ne connait plus les spécificités de chaque pièce ou boisson et qu'il n'y aura que la fabrique à modifier pour rajouter des nouveaux types pour chaque. (Whaou, je vais essayer de parler moins pour la suite ^^)

2)
Pour la classe Piece, moui, si il y a eu des sous-entendus pour vous faire mettre un héritage on va contrarier personne. Pour le moment je vois juste la valeur qui change d'une pièce à l'autre, donc seulement les données et pas le comportement. Pour il n'y a pas a redéfinir une classe pour ça mais juste à passer un paramètre au constructeur. On le fera avec de l'héritage si il faut.

3)
Pour la création d'un pièce, il n'y a pas d'obligation. On peut faire un enum pour lister les types de piece que l'on peut demander à la fabrique. (Mais on fige le programme de l'utilisateur à un nombre de possibilité, ici on ne va pas non plus en mourir...) Sinon la fabrique peut permettre de lister les valeurs possibles pour lesquelles on peut lui demander des pièces. Sinon on peut mettre dans la documentation, les valeurs possibles sont... tout autre valeur renvoit un pointeur NULL. A toi de voir.

4)
Un template (c'est juste une intro) c'est un truc supra balèse en C++. On va prendre un exemple tout bête. Une liste chaînée. En C liste chaînée sur mes éléments, void *, je caste etc. En C++ non. On définit une classe avec un élément générique. Une liste chaînée d'élément de type Z. Ensuite on pourra déclarée une variable qui est de type liste chaînée de Boisson, une autre de Piece etc. Je mets juste un petit exemple de fonction template.

template <typename _TYPE >
_TYPE maFonctionPlus( _TYPE a, _TYPE b )
{
return a + b;
}

ensuite je peux écrire:
int s = maFonctionPlus( 2, 4 );
float s = maFonctionPlus( 2.0f, 4.0f );

maFonctionPlus( 2, 4.0f ); // erreur, le compilateur ne peut pas déterminer le type de _TYPE

Globalement cela sert surtout à écrire moins de code et à garder le typage de ses variables (Pas que j'aime pas le void * mais bon...)

Bientôt tu utiliseras des templates. Ceux de la stl (Standard Template Library) notamment le vecteur ou la liste chaînée justement...

5)
Flute, je voulais garder la monnaie. >_<
Il va y avoir de la réflexion intense mais ça ira. Pas long mais intense...

Toujours pareil, tu décides, je ne sais même pas le temps restant. J'aime les choses bien faites mais... Tu restes chef, chef, je veux pas te mettre à la bourre. : /

A bientôt,
M.
-1
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
7 avril 2008 à 19:17
Nous sommes le demain d'hier donc me revoilà,

Pour ce qui est du code, quelques petites choses:

1) boisson.h

- Les includes devraient être dans le ifndef (/ if !defined)
- Le nom de la macro utilisé pour le if !defined pourrait être en majuscules.
- Il manque la majuscule au nom de la classe.
- La section privée est placée avant la section publique alors qu'elle ne sert qu'a celui ou celle qui a codé la classe. Dans un menu au restaurant on ne te met pas les ingrédients puis le nom du plat mais bien l'inverse. D'abord l'important, la description interne après.
- La méthode selectionner passe un objet par copie, c'est mal. (au choix, référence ou pointeur et const si possible)
- Il y a des méthodes qui peuvent être const. (Au moins selectionner pour le moment)
- Les paramètres ne sont pas nommés. Le .h n'est pas la documentation mais ce n'est pas un prétexte pour masquer la manière dont on doit se servir de ta classe.
- Je mets beaucoup de choses mais c'est normal je fais mon méchant, en vrai je ne suis pas comme ça. ^^"
- Une dernière chose... Si je mets une cannette comme ça toute seule devant toi sur ta table et que je te dis "insère là" t'en fais quoi ? A la limite insérer dans un distributeur, je comprends, ou mieux, dans un distributeur, insérer une cannette. Insérer une cannette toute seule ou même avec un nombre n'est pas assez explicite à mon goût.

2) boisson.cpp

- Au début de selectionner, un seul std::cout suffirait pour tout le menu. (Et en plus je pinaille, je l'avais pas dit ?)
- Une fois un prof (de POO pour replacer les choses dans leur contexte :P) a dit: un "switch" est le signe d'une mauvaise conception objet. Il faut bien avouer que si tu veux ajouter un nouveau type de boisson tu as déjà deux méthodes à modifier...
- Ton switch a ni break ni return. (Mais comme je n'ai pas compris la fonction...)
- Un switch ne marche pas avec des chaînes de caractères.
- Il y a un ; en trop après la définition du destructeur.

Surtout, il n'y a vraiment pas de quoi paniquer ou quoi que ce soit d'autre pour ton projet, il y a bien quelques points que j'ai mis là qui peuvent franchement tomber à l'eau pour le moment. Déjà personne n'a dit que j'étais une référence et d'autre part il vaut mieux se focaliser plus sur les classes nécessaires que de faire le tatasse sur des const par exemple.

idem ici, je passe de temps en temps sur le forum, si il y a le moindre point qui n'est pas absolument clair n'hésite pas à redemander. Je dis pas qu'il sera forcément à appliquer mais au moins comprendre l'idée. (et pouvoir donner ton avis ;-))

Voili voilou,
M.
-1
Utilisateur anonyme
9 avril 2008 à 00:45
Merci de ta remarque concernant les ifdefined, j'avais un doute là-dessus :)

Les macros ont été générés automatiquement, ça ressemble à ça après avoir appliqué la méthode UPPER ;)

#if !defined(OBJECTORIENTEDMODELBOISSON_H)
#define OBJECTORIENTEDMODEL_1_BOISSON_H


Désolée, j'ai mal copié le fichier achat.h, le revoilà :

#if !defined(OBJECTORIENTEDMODELACHAT_H)
#define OBJECTORIENTEDMODEL_1_ACHAT_H

#include<iostream>
#include<string>

class Achat
{
private:
   int somme;

public:
   Achat(int);
   ~Achat();

};

#endif


J'ai modifié les noms des classes pour qu'elles commencent en majuscule, ça sera pareil pour les nom des méthodes (normalement oui quand il s'agit des constructeurs ou des destructeurs car ils prennent le même nom que celui de la classe) ?

J'ai replacé la section privée après la publique, j'avais l'habitude de placer le private avant le public.

La méthode selectionner passe un objet par copie, c'est mal. (au choix, référence ou pointeur et const si possible)
-->
void selectionner(); //pointeur sur boisson 

Ce pointeur passé en paramètre serait de type boisson ? Je déteste les références et je ne les ai jamais compris l'utilité, j'ai pas pu les digérer (c'est de ma faute). Pour const, tu veux dire une constante ou un constructeur ? J'opte pour le pointeur, ça serait plus facile ^^

- Il y a des méthodes qui peuvent être const. (Au moins selectionner pour le moment)
--> const serait un type de retour ? Désolée, j'ai pas bien compris :S


- Les paramètres ne sont pas nommés. Le .h n'est pas la documentation mais ce n'est pas un prétexte pour masquer la manière dont on doit se servir de ta classe.
--> OK, je les ai renseignés :)

A la limite insérer dans un distributeur, je comprends, ou mieux, dans un distributeur, insérer une cannette.

--> Donc, faut supprimer cette méthode (inserer) de la classe Boisson et la prévoir dans la classe Distributeur ?

Dans boisson.cpp, la méthode selectionner est là pour permettre au client de faire son choix parmi la liste des boissons, s'il choisit le chiffre 1, c'est un Coca qu'il a sélectionné. Le cout demande au client de vérifier son choix; par exemple : un message s'affiche : "Vous avez choisi telle ou telle boisson, ça dépend du chiffre qu'il a choisi....

- Il y a un ; en trop après la définition du destructeur.
--> C'est la prof qui nous a demandés de mettre toujours des ; à la fin des fichiers .h et .cpp; c'est pas vrai ?

Mille merci de ton temps et au contraire, tu n'es pas méchant et tu pinailles pas :)

Pour te rafraichir ;)
-1
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125 > Utilisateur anonyme
9 avril 2008 à 02:40
#if !defined(OBJECTORIENTEDMODELBOISSON_H)
#define OBJECTORIENTEDMODEL_1_BOISSON_H

Ah non, là je suis déçu :P

Personnellement je suis fan du :
#ifndef _MON_FICHIER_H
#define _MON_FICHIER_H
...
#endif//_MON_FICHIER_H

Mais il paraît que le #if !defined est mieux. Par contre if not defined ( A ) define B... Bon aller on met ça sur le coup de la fatigue. Tu as raison, je suis bien trop gentil. :D



Le fichier achat.h mêmes remarques que les autres (nom des paramètres, private/public) mais ça devrait être bon pour la suite.

Les majuscules aux noms de méthodes c'est à ta préférence. Au début je n'en mettais pas et puis là les autres en mettent, c'est plus pour uniformiser le code. Il faudra peut-être rechanger un jour.

Je mettais aussi mes private avant les public au départ, maintenant je trouve ça vraiment pas logique.


La méthode selectionner.
Les références sont identiques aux pointeurs sauf que le compilateur fait des vérifications en plus pour que la valeur soit valide. (Et des fois il le voit pas ^^) Le pointeur lui accepte la valeur NULL.
Outre le fait qu'il n'y a pas besoin au début de chaque fonction de vérifier si tous les pointeurs passés en paramètres sont non NULL, si je n'aime pas les références c'est pour ça :

selectionner( maBoisson ); // Copie ou référence ?
selectionner( &pMaBoisson ); // aucun doute.

Pour ce qui est de la méthode selectionner. Je ne partage pas l'avis général de la classe. Pour moi une Boisson c'est une boisson avec un type, un nom etc. Pour toi c'est des types, des quantités... C'est pas ma cannette sur la table quoi. C'est peut-être juste le nom de la classe qui ne me revient pas par rapport à ce qu'elle fait.


Pour la version actuelle de selectionner (que je déplacerais) c'est des constantes :
void selectionner( const Boisson *pBoisson ) const;
Le paramètre est un pointeur vers une boisson constante. Le fait de selectionner la boisson ne la modifiera pas.
La méthode est déclarée constante. Une méthode const signifie qu'elle ne modifie pas l'objet. (celui du this)

Les const, soit on les mets tous, soit il faut oublier. De toute façon le compilateur sera tellement ch*** qu'il demandera de les faire sauter un à un si on en a mis que la moitié de ce qu'il faut. L'intérêt de la méthode déclarée const est tout simplement que c'est une méthode que l'on peut utiliser sur un objet déclaré constant. L'intérêt de l'objet constant est juste qu'on est bien content de savoir que si on passe son objet à une fonction elle ne va pas le bousiller avant de nous le rendre.


Je déplacerais en effet la méthode inserer.


Il y a un truc que j'avais pas dit mais euh enfin... c'est que les std::cout ils peuvent pas rester là ma bonne dame... ^^ Et ma super interface 3D en openGL alors, elle va faire quoi avec tes std::cout ? : /
Tu me brises le coeur.


Bon bon, on aura donc des ; à la fin des .h et des .cpp pour faire plaisir à médème la prof mais bon... Je dois avouer que c'est la première fois que je vois la chose...

Héhé, Trop cool le cocktail. Merci ^^

Dans la série c'est toi la chef, chef : Cela irait peut-être mieux pour montrer le code, d'envoyer les fichiers par mail plutôt que de les copier-coller un à un sur le forum. (Ou msn si tu préfères, ça me fera une raison de plus pour penser à le mettre en route plus souvent) Ceci dit ça te prive des avis des autres membres du forum : / (Je crois d'ailleurs que j'écris trop, quand ils voient le bazar ils abandonnent ^^) Sinon j'aime pas trop les adresses mail qui traînent, tu laisseras un mot dans ma boite sur CCM si ça te chante.

Sinon pour les réveils difficiles, tu as un truc ? ;-)

A bientôt,
M.
-1
mamiemando Messages postés 33545 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 25 février 2025 7 829
10 avril 2008 à 20:29
Khaoula, tu m'as demandé de l'aide, j'ai un peu plus de temps à te consacrer. Pour éviter de dérouler tout le fil, il faudrait me dire les points sur lesquels tu bloques actuellement. A moins que tu aies déjà résolu ton problème ;-)

Bonne chance
-1
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
10 avril 2008 à 22:53
Re ^^

Dans ce cas il vaudrait mieux faire une classe pour la pièce ou alors au moins une classe pour englober un type de pièce et le nombre de pièces de ce type présent dans le distributeur. Ca fait un peu une classe pour englober un float ou un char * dans le cas des boissons mais ça fait 'objet'. On pourrait mettre le prix aussi dans les boissons, à voir...

Il reste encore ce choix de représenter séparément chaque pièce ou chaque type de pièces. Les deux peuvent être faits avec des classes mais pour moi je trouve encore que l'un fait plus simulation et l'autre implémentation concrète. Ce qui me gène un peu dans le cas où chaque pièce est représentée par une instance distincte, c'est justement que toutes les instances d'une même classe resteront identiques d'un bout à l'autre du programme.


M.

PS: On s'est croisés tout à l'heure mais je te laisse prendre les devants quand tu as besoin ;-)

Sur ce, j'ai du sommeil à rattraper, bonne nuit tout le monde !
-1
mamiemando Messages postés 33545 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 25 février 2025 7 829
11 avril 2008 à 10:14
Résumé du message en privé de khaola : en cours on a pas vu la STL

Pour l'histoire de la STL c'est vraiment le gros intérêt du C++. C'est plutôt plus important que les héritages.

Je te fais un cours "accéléré" sachant que le reste il faut s'entraîner et regarder les fonctions ici :
https://community.hpe.com/t5/custom/page/page-id/HPPSocialUserSignonPage?redirectreason=permissiondenied&referer=https%3A%2F%2Fcommunity.hpe.com%2Ft5%2FServers-Systems-The-Right%2FSGI-com-Tech-Archive-Resources-now-retired%2Fba-p%2F6992583
https://community.hpe.com/t5/custom/page/page-id/HPPSocialUserSignonPage?redirectreason=permissiondenied&referer=https%3A%2F%2Fcommunity.hpe.com%2Ft5%2FServers-Systems-The-Right%2FSGI-com-Tech-Archive-Resources-now-retired%2Fba-p%2F6992583

La partie vraiment utile de la STL qu'il faut maîtriser ce sont les containers (sections 3.2.1 et 3.2.2). Les plus importants sont :

std::string : des chaînes de caractères virtuellement sans taille limite
opérateur [ ], size, ...

std::vector<T> : grosso modo les tableaux du C mais encore une fois on peut facilement les agrandir, accès et insertion en O(1) (les cases sont adjacentes en mémoire). Méthodes intéressantes :
push_back, opérateur [ ], size, resize, ...

Exemple :
std::vector<unsigned> v(10); // un vecteur de taille 10
v[0] = 5;
v[1] = 2;
...
for(std::size_t i=0;i<v.size();++i) std::cout << v[i] << std::endl;

std::set<T> : le type T doit être ordonné par un opérateur < (que tu peux définir). Si tu veux utiliser autre chose que < c'est possible en définissant un "foncteur", c'est à dire une autre relation d'ordre. Les éléments sont insérés de manière unique (par unique j'entends non différentiable avec <) et ordonnée. Si tu t'autorises les objets en plusieurs exemplaires std::multiset. Accès en O(log(n)) (repose sur un arbre rouge noir)
begin, insert, end, find

Exemple :
std::set<unsigned> s;
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(1);
s.insert(2);
std::set<unsigned>::const_iterator
  sit (s.begin()),
  send(s.end());
for(;sit!=send;++sit) std::cout << *sit << std::endl; // affiche 1,2,5
std::cout << s.size() << std::endl; // affiche 3

std::map<K,D> : K est le type d'une clé et doit au même titre que pour un std::set être ordonné (par défaut par <, comme le std::set, sinon avec un foncteur). D est un type quelconque (la donnée). Le [ ] donne accès en lecture écriture à une clé et la crée si elle n'est pas présente dans la map. find donne accès en lecture écriture ou en lecture seule (pour une const std::map) et ne crée pas la clé si elle est absente. Dans la STL un find retourne toujours l'iterator (const_iterator) égal à end() s'il n'a pas trouvé la clé. Exemple :
std::map<std::string,int> calendrier;
calendrier["janvier"] = 1;
calendrier["fevrier"] = 2;
...
std::map<std::string,int>::const_iterator fit = calendrier.find("janvier");
if (fit != calendrier.end()) std::cout << "mois = " << fit->first << "\t" << fit->second << std::endl;

std::list<T> : une liste chaînée... Accès en O(n). Sert plus rarement car en général on aime bien avoir une bonne complexité et ranger nos objets par exemple avec un std::set ou un std::multiset.

Dans ton cas tu vois que la caisse correspond à une simple map dont la clé est la valeur d'une pièce et la donnée le nombre de pièce de cette valeur. Avec [ ] tu peux facilement incrémenter une valeur de pièce (même absente, auxquelle cas initialisée avec le unsigned par défaut, 0) pour la mettre dans la caisse.

Pour décrémenter c'est plus subtil. Il faut déjà vérifier que la valeur de pièce existe avec un find. Si c'est le cas pour éviter à [ ] de reparcourir la map, on réutilise directement l'iterator du find. Comme ici on va modifier la map il faut utiliser le find qui utilise un iterator (lecture écriture) et pas un const_iterator (lecture seule). Appelons cet iterator fit. fit->first référence la clé (la valeur de la pièce) et fit->second la donnée (le nombre de pièce). S'il est nul je n'ai plus de pièce de cette valeur. Je ne dois donc pas décrémenter. Sinon je peux :-)

En espérant que ça t'aidera ;-)
-1