C++ template, stack, vector

Résolu/Fermé
lavoiekeven Messages postés 22 Date d'inscription dimanche 7 octobre 2007 Statut Membre Dernière intervention 21 septembre 2009 - 26 oct. 2007 à 16:51
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 19 mars 2008 à 17:55
Bonjour,
je dois faire un programme mais j'ai 9 erreurs que je ne sais pas comment resoudre, sa commence dont tres mal, j'aurais donc besoin de savoir c'est quoi l'erreur

#include <vector>
#include <iostream.h>
#include <stack>
using namespace std;

template <class T>
class Postfixe
{
Private:
stack<T> Pile;
vector<T> tableau;
Public:
// lit l'expression à évaluer, à partir du clavier, dans tableau et valide si l'expression ne contient que
// les caractères ci-dessus, à savoir les nombres entiers composés de caractères nombres, et les opérateurs ci-dessus.
bool postfix(vector<T> tableau );

// (bonus) teste si l'expression lue contient un nombre valide de parenthèses
bool valider_expression(vector<T> tableau);

// transforme les nombres lus en caractères en valeurs numériques
void transformerennombres(vector<T> tableau);

// transforme l'expression lue en une expression postfixe.
void transformerenpostfixe(stack<T> Pile, vector<T> tableau);

// affiche la valeur de l'expression lue.l
int evaluer_expression(stack<T> Pile, vector<T> tableau);
};

template <class T>
bool Postfixe<T>::postfix(vector<T> tableau)
{
}

template <class T>
bool Postfixe<T>::valider_expression(vector<T> tableau)
{
}

template <class T>
void Postfixe<T>::transformerennombres(vector<T> tableau)
{
}

template <class T>
void Postfixe<T>::transformerenpostfixe(stack<T> Pile, vector<T> tableau)
{
}

template <class T>
int Postfixe<T>::evaluer_expression(stack<T> Pile, vector<T> tableau)
{
}


Merci
A voir également:

10 réponses

mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
27 oct. 2007 à 05:05
1) Pour commencer les fonction template doivent être implémentées dans le header. Or il ne faut pas pas faire de "using namespace std" dans le "global scope" d'un header, car sinon tout fichier qui inclue ce header sera victime du "using namespace std". C'est gênant le jour ou tu es dans un namespace avec des noms en conflits avec des classes de la librairies standard. Pour résumer : il ne fait jamais utiliser de "using namespace std;" au début d'un header.

Dans ton cas la plupart des fonctions que tu as écrites (evaluer_expression, valider_expression...) ont un type de retour donc il faut au moins mettre un return pour que ça compile. Par ailleurs je vois que tes prototypes passent des stack, des vector etc. Le problème c'est que comme en C, les paramètres d'une fonction sont des recopies de l'objet original. Ca veut dire que si le paramètre est volumineux en mémoire, il est très long à recopier (et donc l'appel de la fonction s'en trouve affecté). C'est pourquoi en C++ on utilise très souvent des référence (&) :
template <class T>
bool Postfixe<T>::valider_expression(vector<T> & tableau)
{

  return true;
} 

Syntaxiquement parlant ça ne change rien à ton code, tout se passe comme si tu avais passé l'objet lui-même, mais en réalité tu manipules directement son adresse (et donc tu gagnes en efficacité !). De plus comme le paramètre n'est pas modifié il est mieux de préciser que la fonction ne le modifie pas en rajoutant un const. De même si la méthode ne modifie pas l'instance de classe à laquelle se raccorche (*this), tu es sensé le préciser en rajoutant un const en fin de prototype.
template <typename T>
bool Postfixe<T>::valider_expression(const vector<T> & tableau) const
{
  std::cout << "mon vecteur est de taille " << tableau.size() << std::endl;
  return true;
} 

Ici j'ai changé "class T" par "typename T". Les deux syntaxes marchent mais je trouve la seconde meilleure, car ton type template peut être un type de base, une structure et pas forcément une classe.

A présent allons un peu plus loin. Une classe template ne se compile pas à proprement parler. En fait tu compiles la classe template pour chaque type d'utilisation. Par exemple si tu utilises des std::vector<int>, des std::vector<double> etc tu compiles autant de classes vector. Ainsi un type template n'a pas vraiment de sens durant la phase de précompilation, en particulier si tu dois accéder à des champs d'une classe template. Par exemple supposons que je veuille utiliser le typedef "const_iterator" d'un std::vector<T>. A ce stade le type std::vector<T> n'est pas encore défini, donc pour feinter le compilateur on utilise le mot clé typename :
template <typename T>
bool Postfixe<T>::valider_expression(const vector<T> & tableau) const
{
  std::cout << "mon vecteur est de taille " << tableau.size() << std::endl;
  // Le type en italique dépend d'au moins un paramètre template (ici T) 
  // donc doit être précédé d'un typename si on veut accéder
  // à un de ses champs (ici le type "const_iterator").
  typename std::vector<T>::const_iterator
    vit (tableau.begin()),
    vend(tableau.end());
  for(;vit!=vend;++vit) std::cout << *vit << ' ';
  std::cout << std::endl;
  return true;
}

Pour le typename retiens juste que quand tu as besoi de l'opérateur :: et que le type devant :: dépend d'un paramètre template, il faut un typename. Tu peux tout à fait faire un tyepdef si tu le souhaites :
typedef typename std::vector<T>::const_iterator my_const_iterator;
my_const_iterator vit(v.begin()),vend(v.end());

2) Ensuite attention les mots clés private, protected, public s'écrivent en minuscule. De manière générale en C++ tout s'écrit en minuscule. Mais il arrive que les notations "java" (mettre une majuscule pour les noms de classe, écrire les noms de fonction comme ceci : ceciEstUneFonction) sont utilisées. Personnellement je n'aime pas trop mais après chacun rédige son code à son idée, moi j'en mets juste pour les paramètres templates (par exemple T dans ton exemple)

3) Si tu déclares une fonction dans un header sans l'implémenter (par exemple bool postfix(vector<T> tableau ); est déclarée mais pas implémentée), il faut l'implémenter au dans le fichier source (plop.cpp) correspondant à ton header (plop.hpp)

4) Enfin il ne faut pas inclure <iostream.h> mais <iostream>


Bonne chance
4
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
28 oct. 2007 à 02:23
C'est syntaxiquement bon modulo trois problèmes

1) Tes fonctions prennent des paramètres qui ne sont ni des références, ni des pointeurs. Ca veut dire que si tu modifies un paramètre dans une fonction, ces modifications n'auront effet qu'au sein de cette fonction. En effet tu ne dois pas oublier qu'en C et en C++ quand tu appelles une fonction, les paramètres utilisés par la fonction sont des recopies. Exemple :
#include <iostream>

void incrementer(int x){
  ++x;
  std::cout << "incrementer : " << x << std::endl;
}

void incrementer1(int & x){
  ++x;
  std::cout << "incrementer1 : " << x << std::endl;
}

void incrementer2(int *x){
  ++(*x);
  std::cout << "incrementer2 : " << x << std::endl;
}

int main(){
  int x = 0;
  incrementer(x);
  std::cout << "main : " << x << std::endl;
  incrementer1(x);
  std::cout << "main : " << x << std::endl;
  incrementer2(&x);
  std::cout << "main : " << x << std::endl;
  return 0;
}

.. donne à l'exécution :
incrementer : 1
main : 0
incrementer1 : 1
main : 1
incrementer2 : 2
main : 2

Au travers de cet exemple tu vois bien que x n'est modifié par incrementer() qu'au sein de son corps de fonction, car incrementer() ne manipule qu'une recopie de la variable x du main(). C'est l'écueil que tu rencontreras par exemple dans ta fonction transformerennombres()
- Dans incrementer1() on spécifie au travers de la référence qu'il ne faut pas recopier la variable. C'est la technique qu'on utilise le plus souvent en C++
- Dans incrementer2() on passe directement l'adresse de la variable de la variable x. Pour être plus précis, on devrait dire qu'on recopie l'adresse de la variable x, mais que la valeur de cette adresse est bien toujours l'adresse de la variable x du main. Comme les références n'existent qu'en C++ c'est la seule façon de s'en sortir en C.

Quoiqu'il en soit retiens qu'on ne passe que des adresses ou des pointeurs car on manipule bien ce qu'on croit, et surtout on évite de recopier des gros volumes de données. Typiquement dans ta méthode evaluer_expression() il est inutile de recopier Pile et tableau surtout si ces deux structures sont susceptibles d'être grosses et donc longue à recopier.

D'une manière générale en C++ on ne passe que des références ou des pointeurs car on veut être potentiellement capable de modifier les paramètres (comme dans incrementer1()), et on veut éviter de recopier des gros paramètres pour rien (comme dans evaluer_expression()).

Etant donné que tu ne fais plus de recopie d'objet mais que tu les manipules directement, il est dès lors important de préciser les paramètres que ta fonction est susceptible de modifier (paramètres modifiable ou "donnée résultat"), et ceux qui ne seront jamais modifiés (paramètres constant, identifiés par le mot clé const). En particulier, *this est un paramètre de ta classe à part entière, et il est important de dire s'il est constant ou pas.

2) Ensuite il faut bien voir que tu travailles dans une classe template, donc chaque membre de fonction doit être implémentés dans le fichier dans lequel tu as écrit ta classe. Tu peux soit le faire hors de la classe comme tu avais commencé à le faire dans ton premier post, soit directement dans la classe elle même.

3) Attention ton constructeur doit porter le nom de ta classe. Donc soit tu appelles ta classe "postfixe", soit tu appelles ton constructeur "Postfixe". Enfin un constructeur n'a pas de valeur de retour.

Par ailleurs je n'ai pas très bien compris pourquoi tu passes systématiquement "Pile" et "tableau", qui sont deux attributs de ton instance, donc directement accessible. Dans le doute je les ai viré, car si j'ai bien compris ce que tu veux faire, ces méthodes vont tester les attributs d'une instance postfixe p, les modifier, et les afficher. Or dans une méthode C++, la méthode peut accéder aux attributs de l'instance à laquelle elle se rattrache (ici l'instance p)
#include <iostream>

// Début de postfixe.hpp
#include <vector>
#include <stack>

template <typename T>
class postfixe
{
    public:
        typedef std::stack<T> pile_t;
        typedef std::vector<T> tableau_t;
    protected:
        pile_t pile;
        tableau_t tableau;
    public:
        postfixe(const tableau_t & tableau0):
            tableau(tableau0) // recopie tableau0 dans l'attribut tableau
        {}

        bool valider_expression() const { // const car a priori on ne modifie ni pile, ni tableau
            std::cerr << "valider_expression: not yet implemented" << std::endl;
            return false;
        }

        void transformer_en_nombres() { // pas const car on modifie pile ou tableau
            std::cerr << "transformer_en_nombres: not yet implemented" << std::endl;
        }

        void transformer_en_postfixe() { // pas const car on modifie pile ou tableau
            std::cerr << "transformer_en_postfixe: not yet implemented" << std::endl;
        }

        int evaluer_expression() const { // const car a priori on ne modifie ni pile, ni tableau
            std::cerr << "evaluer_expression: not yet implemented" << std::endl;
            return 0;
        }
};
// Fin de postfixe.hpp

int main(){
    postfixe<int>::tableau_t tableau0;
    postfixe<int> p(tableau0);
    p.valider_expression();
    p.transformer_en_nombres();
    p.transformer_en_postfixe();
    p.evaluer_expression();
    return 0;
}

Pour le moment si postfixe n'est utilisée que dans ton fichier source principal (main.cpp par exemple) tu peux te contenter de tout mettre dans main.cpp. Mais à terme ta classe devra être transférée dans un fichier (par exemple postfixe.hpp), et main.cpp devra inclure postfixe.hpp.


Bonne chance
1
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
28 oct. 2007 à 23:14
Il y a mieux pour lire une chaine : tu peux utiliser directement un std::string.
#include <iostream>
#include <string>
#include <vector>

int main(){
    std::string operation;
    std::cout << "opération ? ";
    std::cin >> operation;
    std::size_t len = operation.size();
    std::vector<char> tableau0;
    tableau0.reserve(len);
    for(std::size_t i=0;i<len;++i) tableau0.push_back(operation[i]);
    //...
    return 0;
}

Il faut reserver la mémoire quand tu utilises push_back() et quand c'est possible, car un push_back est susceptible de faire un realloc quand le vecteur grossit... c'est mauvais d'un point de vue performance si le vecteur est gros.

Pour éviter ce problème on peut utiliser la méthode reserve qui place le vecteur en mémoire de sorte à éviter ces realloc. Un reserve ne modifie pas la taille du vecteur.
Une autre possibilité consiste à construire le vecteur directement avec la bonne taille (ou à le resize), mais du coup le vecteur fait la nouvelle taille.
Pour plus d'infos sur les vecteurs :
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

Bonne chance
1
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
4 nov. 2007 à 23:43
Ah non ça se fait au niveau du constructeur :
std::vector<int> v(50,0);

Sinon tu peux le faire avec std::fill (si tu as redimensionné ton vecteur, ou construit ton vecteur à la bonne taille).

Voici un exemple :
#include <vector>
#include <iostream>
#include <algorithm> // std::fill

template <typename T>
void write_vector(
    const std::vector<T> & v,
    std::ostream & out = std::cout
){
    const std::size_t n = v.size();
    out << '[';
    for(std::size_t i=0;i<n;++i){
        out << ' ' << v[i];
    }
    out << " ]" << std::endl;
}

int main(){
    std::vector<int> v0(10); // les 10 entiers sont initialisés avec l'entier par défaut (0)
    write_vector(v0);
    std::vector<int> v(10,1); // les 10 entiers sont initialisés à 1
    write_vector(v);
    std::fill(v.begin(),v.end(),2);
    write_vector(v);
    return 0;
}

Ce qui donne à l'exécution :
[ 0 0 0 0 0 0 0 0 0 0 ]
[ 1 1 1 1 1 1 1 1 1 1 ]
[ 2 2 2 2 2 2 2 2 2 2 ]

Bonne chance
1

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

Posez votre question
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
19 mars 2008 à 17:55
C'est hors sujet mais pour faire bref de nos jour on utilise <iostream> au lieu de <iostream.h> (obsolète. Du coup les classes de la stl sont dans le namespace std. Ce qui donne :
#include <iostream>

void swap(float *p1,float *p2){
/*
  // je ne sais pas ce que tu as voulu faire ici mais ça n'a pas de sens
  // la variable p2 est passée en paramètre et est masquée par une variable locale p2
  // la boucle while peut tourner à l'infini (du moins jusqu'à faire une seg fault
  while(*p1<*p2) p1++;
  float *p2=p1;
*/
}

int main( ){
  float a,b,c;
  std::cout<<"xxxxxxxxxxx";
  std::cin >> a >> b >> c;
  swap(&a,&b);
  swap(&a,&c);
  swap(&b,&c);
  std::cout << "xxxxxxxxxxxxxxx";
  std::cout << a << ' ' << b << ' ' << c << std::endl;
  return 0; // code d'exécution du programme
}

Merci d'ouvrir un NOUVEAU sujet pour tout problème différent de celui de départ (post <1>)

Bonne chance
1
lavoiekeven Messages postés 22 Date d'inscription dimanche 7 octobre 2007 Statut Membre Dernière intervention 21 septembre 2009
27 oct. 2007 à 14:35
Merci pour tes corrections il m'aide beacoup a comprendre la matiere cependant, que il ya un erreur en debut de programme que je n'arrive pas a trouver :



C:\devoir2\dev2.cpp(6) : error C2143: syntax error : missing ';' before '<'
C:\devoir2\dev2.cpp(6) : error C2143: syntax error : missing ';' before '<'
C:\devoir2\dev2.cpp(7) : error C2143: syntax error : missing ';' before '{'
C:\devoir2\dev2.cpp(7) : error C2447: missing function header (old-style formal list?)




#include <vector>
#include <iostream>
#include <stack>

template <typename T>
class Postfixe <T>
{
private:
stack <T> Pile;
vector <T> tableau;
public:
// lit l'expression à évaluer, à partir du clavier, dans tableau et valide si l'expression ne contient que
// les caractères ci-dessus, à savoir les nombres entiers composés de caractères nombres, et les opérateurs ci-dessus.
bool postfix(vector<T> tableau );

// (bonus) teste si l'expression lue contient un nombre valide de parenthèses
bool valider_expression(vector<T> tableau);

// transforme les nombres lus en caractères en valeurs numériques
void transformerennombres(vector<T> tableau);

// transforme l'expression lue en une expression postfixe.
void transformerenpostfixe(stack<T> Pile, vector<T> tableau);

// affiche la valeur de l'expression lue.l
int evaluer_expression(stack<T> Pile, vector<T> tableau);
};


Merci d'avance Keven !!!!!!!!!!!!!!!!!
0
lavoiekeven Messages postés 22 Date d'inscription dimanche 7 octobre 2007 Statut Membre Dernière intervention 21 septembre 2009
27 oct. 2007 à 20:26
j'ai ajoute des std:: les erreurs disparait mais je veux savoir si c'est correcte



#include <vector>
#include <iostream>
#include <stack>

template <typename T>
class Postfixe
{
private:
std::stack <T> Pile;
std::vector <T> tableau;
public:
// lit l'expression à évaluer, à partir du clavier, dans tableau et valide si l'expression ne contient que
// les caractères ci-dessus, à savoir les nombres entiers composés de caractères nombres, et les opérateurs ci-dessus.
bool postfix(std::vector<T> tableau );

// (bonus) teste si l'expression lue contient un nombre valide de parenthèses
bool valider_expression(std::vector<T> tableau);

// transforme les nombres lus en caractères en valeurs numériques
void transformerennombres(std::vector<T> tableau);

// transforme l'expression lue en une expression postfixe.
void transformerenpostfixe(std::stack<T> Pile, std::vector<T> tableau);

// affiche la valeur de l'expression lue.l
int evaluer_expression(std::stack<T> Pile, std::vector<T> tableau);
};
0
lavoiekeven Messages postés 22 Date d'inscription dimanche 7 octobre 2007 Statut Membre Dernière intervention 21 septembre 2009
28 oct. 2007 à 20:05
Merci sa m'aide sérieusement a comprendre comment les classe template fonctionne il y a juste un dernier petit point ou je suis mêlé.

si j'ai bien compris:
pour initialiser le vecteur dans la classe, j'ai qu'a initialiser tableau0 et pour l'initialiser ce dernier il faut que j'entre mon scripte entre postfixe<int>::tableau_t tableau0; et postfixe<int> p(tableau0);

voici ce que j'en conclu mais quand que j'essaie de voir s'il est bien initialiser, il me sort nimporte quoi


void main()
{
postfixe<int>::tableau_t tableau0;
char reponse[50];
int i=0;

std::cout<<"Veuillez entrer votre operation :"<<std::endl;
std::cin>>reponse;

while(reponse[i] != '\0')
{
tableau0.push_back(reponse[i]);
i++;
}
postfixe<int> p(tableau0);
p.valider_expression();
p.transformer_en_nombres();
p.transformer_en_postfixe();
p.evaluer_expression();
}

Merci Keven
PS: je suis debutant avec les verteur !!!!
0
lavoiekeven Messages postés 22 Date d'inscription dimanche 7 octobre 2007 Statut Membre Dernière intervention 21 septembre 2009
4 nov. 2007 à 01:01
Bonjour,
Merci beacoup pour ton aide j'ai juste un dernier petit point comment j'peut faire pour réinitialiser le vecteur a 0;
j'ai essayer avec vecteur.erase(); et vecteur.clear(); mais rien ne fonctionne


Merci Keven
0
je suis entrain de realiser un programme et j en suis au point qu il me reste une erreur a resoudre pour pouvoir proceder a la compilation.mais je n arrete pas de faire face a cette erreur:"E:\临床医学一\computer\nveau cours et ex\sss\43107139-3-a.cpp(6) : error C2447: missing function header (old-style formal list?)"
quelqu un pourrait il m aider a corrriger mon programme s ilvous plait?
le voici#include<iostream.h>


void swap(float *p1,float *p2);

{

while(*p1<*p2)p1++;
float*p2=p1;

}

void main( )
{
float a,b,c;
cout<<"输入三个实数:";
cin>>a>>b>>c;
swap(&a,&b);
swap(&a,&c);
swap(&b,&c);
cout<<"按升序排列:";
cout<<a<<' '<<b<<' '<<c<<'\n';
}
0