je débute exercice pour la POO vos avis

lmoraless
je débute exercice pour la POO vos avis

Bonjour voila je débute dans la POO et j'aimerais m'entrainais. Ainsi j'ai trouver un exercice à faire (sur un site):

"Nous verrons ici comment résoudre un problème de gestion à l'aide de la POO. cas: gestion des températures de différentes villes et statistiques. Pour chaque ville et chaque mois de l'année, on disposera de la température moyenne observée. L'application nous permettra d'effectuer la saisie des températures des différentes villes, elle nous affichera ensuite: la ville la plus froide , la ville la plus chaude, la ville qui subit l'amplitude la plus forte chaque affichage sera constitué du nom de la ville et du chiffre demandé exprimé en degrès (celsius). On propose la liste de classe suivante: ville région application"

Voila ce que j'ai commencé à faire:

fichier.h(definition des class)

#include<iostream>
#include<string>

using namespace std;

class region
{
      private:
      string region2;
      string ville2;
      
      public:
             region(string region_name,string ville_name);
             string affnom_region();
             string affnom_ville();
             
               
};

class ville
{
      private:
              region *m_region;
              double temperature[12];
  
              
              
              public:
              ville(string name_region,string name_ville,double tabtemperature[12]);
              string affname_region();
              
                   
};

Fichier .cpp (implémentation des class)


#include<iostream>
#include<string>

#include "gestion.h"

using namespace std;

region::region(string region_name, string ville_name)
{
    region2= region_name;
    ville2=ville_name;                  
                      
}

string region::affnom_region()
{
       return region2;
}

string region::affnom_ville()
{
       return ville2;
}

ville::ville(string name_region, string name_ville, double tabtemperature[12] )
{
     temperature[12]= tabtemperature[12];
     
     m_region=new region(name_region,name_ville);              
                    
}

string ville::affname_region()
{
       return m_region.affnom_region();
}

Enfin le main .cpp:

#include <cstdlib>
#include <iostream>

#include "gestion.h"

using namespace std;

int main(int argc, char *argv[])
{
    double tabtp[12];
    for(long tour=0;tour<12;tour++)
    {
             cout<<"temperature :"<<endl;
             cin>>tabtp[tour];
    }
    
    string regiona="PACA";
    string villea="Marseille";
    
    ville ville1();
 
    cout<<endl;
    cout<<"nom de la region : "<<ville1.affname_region()<<endl;
    
    system("PAUSE");
    return EXIT_SUCCESS;
}

Est ce correcte? Y a til des erreurs? qu'est ce que vous en pensez?
Parceque mon compilateur n'accepte pas la métode .affname_region() et
je ne comprends pas pourquoi ? En fait j'aimerais que en créant une ville ca crée une région et que ensuite a partir de la class ville on retrouve le nom de la regionvis
fredericmazue

Quote:
Y a til des erreurs?

Oui quelques unes...

Quote:

métode .affname_region() et
je ne comprends pas pourquoi ?

L'erreur est avant car tu n'appelles pas le contructeur de l'objet comme il faudrait. De plus ville n'a pas de constructeur sans argument dans ton code, alors le compilateur tousse.
Il faudrait:
ville ville1(regiona, villea, tabtp);

Maintenant il y a une subtilité subtile. Pourquoi le compilateur ne tousse-t-il pas d'emblée à la ligne
ville ville1();
puisque le onstructeur par défaut est absent ?
La réponse est que là, il ne vois même pas une instanciation d'objet mais un protoype de fonction :lol:

Ton code ne peux absolument pas marcher:
temperature[12]= tabtemperature[12];
Ne fonctionne pas. C++ n'affecte pas les tableaux.
Tu peux utiliser la fonction memcpy pour copier le contenu d'un tableau dans un autre.
Mais le mieux est encore d'utiliser vector plutôt que les tableaux de style C. Et il est possible d'affecter un vector dans un autre :)

Tu devrais préférer cin.get(); à system("PAUSE");

Ton programme fuit :)

region *m_region; L'objet m_region instancié par new dans le constructeur n'est jamais libéré. tu peux corriger ainsi

#include <memory>
auto_ptr<region> m_region;

et réécrire le constructeur de ville
ville::ville(string name_region, string name_ville, double tabtemperature[12] ) : m_region(0)
{
  // Attention c'est toujours faux là :)
  //temperature[12]= tabtemperature[12];
     
  m_region.reset(new region(name_region,name_ville));             
                   
}


Tu devrais éviter de passer des objets par valeur. Si je reprends le même constructeur

ville::ville(string name_region, string name_ville, // etc

Ici lors de l'appel du constructeur les objets string name_region et name_ville sont dupliqués à partir des argument originaux. Ca ne sert à rien ici car les copies des objets sont ensuite détruites (automatiquement). Mais la copie est coûteuse en mémoire et temps d'exécution.
Comme ici la copie n'est pas souhaitable, il faut passer les objet par référence seulement, comme ceci:

ville::ville(string& name_region, string& name_ville, // etc

De plus le constructeur ne modifie pas les arguments, donc il est bien mieux de les déclarer constants. Je te passe les détails, mais disons que ça laisse les coudées franches au compilateur pour faire des optimisations, et que c'est une bonne pratique pour écrire un code plus sûr, donc finalement:

ville::ville(const string& name_region, const string& name_ville, // etc

Principe à appliquer partout où ça s'applique dans ton code, c'est à dire en beaucoup d'endroit.

Bon je n'ai pas tout dit, faute de temps, mais ça te donne déjà du grain à moudre :)

fredericmazue

J'ai trouvé 5 mn alors je reviens sur ce que je n'ai pas dit dans mon post d'avant.

D'abord quelque chose comme:

double valeur;
cin >> valeur;

N'est absolument pas sécurisé. Si l'utilisateur saisi n'importe quoi, ton programme fera encore plus n'importe quoi. Mais comme les entrées/sorties sont un vrai sujet à elles seules, je laisse de côté pour aujourd'hui.

Ensuite:
On a vu que:
region *m_region;
était générateur d'une fuite mémoire.
Je t'ai donné une solution à base de auto_ptr. Ca marche *MAIS* seulement dans le strict cadre de ton code. Dans un cadre plus général, il faudrait gérer la copie et l'affectation des objets ville. Sans cela il y aurait de gros problèmes. Je pourrais le montrer ici, mais je pense que si tu as écrit

region *m_region;

C'est parce que tu n'a pas su écrire

region m_region;

Ou plutôt que tu l'as écrit mais qu'après tu n'as pas su initialiser l'objet en passant des paramètres au constructeur. Ca se fait dans la liste d'initialisation du constructeur, comme ceci:

ville::ville(const string& name_region, const string& name_ville, const vector<double>& vtemperature)
	: m_region(name_region, name_ville), temperature(vtemperature)
{
}

Comme tu vois, j'ai aussi ajouté plein d'autres choses. Un vector pour remplacer le tableau, ainsi que plein de const là où il était pertinent d'en mettre. Du moins pour ce que tu montres du code. Par exemple dans ce que tu montres, on peut penser que ville ne change pas de nom de region, donc

const region m_region;

est naturel.
Bien employer le mot-clé const est très important en C++

A la fin de mon post tu va donc trouver tout ton code réécrit en C++ ;)

*MAIS* avant de te quitter pour ce soir, je tiens à ajouter une chose, qui ne tient pas cette fois à la manière d'écrire en C++, mais à la POO seule.

Il me semble que tu veux travailler avec des classes se faisant mutellement référence (au niveau des données pas au niveau des instances de classes, du moins si je me base sur le code que tu as donné). Une ville détient un membre région, un région détient un membre ville. Je ne suis pas sûr que ça soit une bonne approche. Ca va vite être ingérable.
Une région contenant un vector de villes semblerait plus adapté.

Voici toutefois ton code ré-écrit:
(remarque la disparition de #include )


#include <iostream>
#include <string>
#include <vector>

using namespace std;

class region
{
private:
	const string region2;
	const string ville2;
  
public:
	region(const string& region_name, const string& ville_name);
	string affnom_region() const;
	string affnom_ville() const;
};

class ville
{
private:
	const region m_region;
	const vector<double> temperature;           

public:
	ville(const string& name_region, const string& name_ville, const vector<double>& vtemperature);
	string affname_region() const;                 
}; 


region::region(const string& region_name, const string& ville_name)
: region2(region_name), ville2(ville_name)
{
}

string region::affnom_region() const
{
	return region2;
}

string region::affnom_ville() const
{
	return ville2;
}

ville::ville(const string& name_region, const string& name_ville, const vector<double>& vtemperature)
	: m_region(name_region, name_ville), temperature(vtemperature)
{
}

string ville::affname_region() const
{
	 return m_region.affnom_region();
} 

int main(int argc, char *argv[])
{
	// j'ai la flemme d'en mettre 12 :)
	vector<double> tabtp(3);
	tabtp[0] = 19.0;
	tabtp[1] = 20.0;
	tabtp[2] = 21.0;
    
	string regiona="PACA";
	string villea="Marseille";
   
	ville ville1(regiona, villea, tabtp);
 
	cout << endl;
	cout<< "nom de la region : " << ville1.affname_region() << endl;
  
	cin.get();
	return EXIT_SUCCESS;
} 

Bienvenue dans le monde de C++ et sur notre forum :).
Ton post me change agréablement de l'infâme et omniprésent Java.

K-lo

Quote:
Par exemple dans ce que tu montres, on peut penser que ville ne change pas de nom de region, donc

const region m_region;

est naturel.
Bien employer le mot-clé const est très important en C++

Et en plus ça soulève une question (de base encore ;)).

Personnellement j'utilise pas assez les "const" (mais vaut mieu tard que jamais) donc je voulais savoir les subtilités pour l'employer : une donnée membre est defini type nom_variable const; lorsque l'on est sur que cette donnée ne sera pas modifier après l'avoir défini ?

Deplus si on a défini une variable "string str1;" la méthode getStr1() sera plutot écrit sous cette forme
string& getStr1() { return str1; } plutot que string& getStr1() const {return str1;}

fredericmazue

Salut k-lo :)

Quote:
type nom_variable const;

Non, c'est

const type nom_variable;

Quote:
lorsque l'on est sur que cette donnée ne sera pas modifier après l'avoir défini ?

On en est pas sûr du tout :lol:
Quand on déclare une variable ou un membre const, cela veut dire que le codeur considère que ça doit être const et donc le rester.
Mais il y a toujours la possibilité d'un petit malin dégage le const par un const_cast. Attention je dis pas que c'est une bonne pratique. Dans ce cas c'en est même une très mauvaise. Selon la norme ça aboutit à un comportement indéterminé. Par exemple ça selon la machine et le compilo ça peut marcher. Ou bien par exemple le compilo s'est appuyé sur const pour placer la variable dans une zone de mémoire en lecture seule et là tu as un plantage :) Tu as aussi la possibilié qu'un fûté vienne attaquer ta variable const avec une petite routine en assembleur :) Donc tu n'es **SUR** de rien.

Par contre en déclarant const:
- tu exprimes clairement en tant qu'auteur du code que c'est const et que ça doit le rester.
- tu peux donc compter que ça restera const si tu as affaire à d'autres codeurs bien élévés et qui connaissent C++. (Oui je sais ça devient rare... mais il en reste encore heureusement :) )
Tu augmentes la sécurité et le cohérence de ton code, le compilateur t'interdisant (sauf si tu joues toi même avec const_cast bien entendu) de modifier une variable const et ainsi de te contredire mille lignes de code et un coup de fatigue plus loin
- tu permets au compilo de procéder tout seul comme un grand, telle que celle que j'ai évoqué plus haut, ou encore la suppression pure et simple d'objets temporaires, ou encore, etc, etc :)

Quote:
Deplus si on a défini une variable "string str1;" la méthode getStr1() sera plutot écrit sous cette forme

string& getStr1() { return str1; }
plutot que

string& getStr1() const {return str1;}

Ce n'est pas si simple
Quand tu écris

string& getStr1() { return str1; }

Tu retournes une référence donc cela veut dire que l'appelant peut modifier l'état interne de l'objet puisqu'il peut modifier le membre référencé. Mais ce n'est probablement pas ce que tu veux car tu ne voudras pas violer les principes d'encapsulation les plus élémentaires en donnant ainsi accès à une variable à priori private.
Si tu écris:

string& getStr1() const {return str1;}

Alors tu annonces que getStrr1 ne modifiera pas l'état de ton objet ce qui est franchement faux puisque on peut modifier la chaîne membre comme dit plus haut donc modifier l'état interne de l'objet. Donc ce que tu dois écrire est:

const string& getStr1() const {return str1;} Et là c'est ok.

En revanche dans le code que j'ai donné dans mon post précédent tu as l'équivalent de:

string getStr1() const {return str1;}

Là on retourne une copie du membre. Si l'appelant modifie la copie, l'état de l'objet reste quand même inchangé et donc la déclaration qui dit que getStr1 ne modifie pas l'état interne de l'objet est ok :)

K-lo

Bah j'ai bien fait de poser la question car c'est pas si simple que ça en faite :lol:

fredericmazue

Mais si :)

K-lo

donc si j'ai bien tout compris :lol:
Je suis dans le cas ou je n'aipas défini ma variable membre avec const donc :

class C1{
private :
string str1C1;

public : 
string getStr1C1() const;
void setStr1C1(const newStr &); 
};

Biensur dans le meilleur des mondes ;)
fredericmazue

Quote:
donc si j'ai bien tout compris

Tu as compris :)
Mais si le membre est const c'est bon aussi :)

PS: Je ne sais pas si on pourra continuer cette discussion très loin. A partir de cet après midi, je vais quitter le forum quelques semaines pour cause de vacances (bien méritées :) )

K-lo

Bonnes vacances et merci ! :D