Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Création d'une class Vecteur, questions et critiques

28 réponses
Avatar
Charly
Bonjour =E0 tous,
pour les =E9tudes, je dois r=E9aliser une class Vecteur g=E9n=E9rique, voic=
i
ce que j'ai fait pour l'instant:
Vecteur.h http://pastebin.com/mSw1VpAL
Vecteur.cxx http://pastebin.com/2rVV3pGe

J'aimerai avec une (ou des) critiques sur mon code, ai-je pris des
mauvaises habitudes ? Y'a t'il des optimisation =E0 faire ? ...

De plus, voici une question qui me pose bien des probl=E8mes:
"Modifiez les traitements effectu=E9s dans votre programme principal
afin d=92afficher la valeur du
produit scalaire des vecteurs saisis (vecteur de =AB float =BB et de =AB
std ::string =BB). Normalement, vous devriez voir une erreur appara=EEtre,
pourquoi ? Supprimez le traitement provoquant cette erreur. Que pouvez
vous d=E9duire sur ce que fait le compilateur lorsque vous utilisez la
g=E9n=E9ricit=E9 ?"

Je comprend bien qu'il n'est pas possible du multiplier des float et
des string mais je ne vois pas comment corriger cette erreur. Comment
faire ?

Merci d'avance pour l'aide.

10 réponses

1 2 3
Avatar
Fabien LE LEZ
On Mon, 15 Mar 2010 09:17:40 -0700 (PDT), Charly
:

pour les études, je dois réaliser une class Vecteur générique, voici


unsigned int dimensions(void) const;



Le "void" est en trop ici. Pour indiquer qu'il n'y a pas de
paramètres, une liste vide suffit :
unsigned int dimensions() const;

(En revanche, il est utile en C.)

friend std::ostream &operator<< <>(std::ostream &out, Vecteur<T> &v);



Pas glop. Généralement, l'opérateur << ne doit pas être ami, mais doit
utiliser les fonctions membres publiques.
Par ailleurs, << ne modifie pas l'objet Vecteur, donc le paramètre
doit être une référence const, ou bien l'objet lui-même (passage par
valeur, qui est moins coûteux qu'il n'y paraît) :

template<class T>
std::ostream &operator<<(std::ostream &out, Vecteur<T> const& v)
{
unsigned int dim = v.dimensions();

out << "[";
for(unsigned int i=0; i<dim-1; ++i)
out << v[i] << ",";
out << v[dim-1] << "]";

return out;
}

Pour l'opérateur =, c'est pas mal, mais généralement on ajoute
carrément une fonction membre (publique) swap :

template<class T>
void Vecteur<T>::swap (Vecteur<T>& autre)
{
assert (autre.m_dim == m_dim);
std::swap (autre.m_values, m_values);
}

template<class T>
Vecteur<T> &Vecteur<T>::operator=(const Vecteur<T> &v)
{
Vecteur temp( v );
swap (temp);
return *this;
}

std::istream &operator>>(std::istream &in, Vecteur<T> &v)
std::cout << "coef " << i << " : ";



Gnii ?
"cout" n'a absolument rien à faire dans un opérateur >>.
Avatar
Fabien LE LEZ
On Mon, 15 Mar 2010 09:17:40 -0700 (PDT), Charly
:

template<class T>
class Vecteur
{
...
Vecteur<T> operator+(const Vecteur<T> &v);
...
};
template<class T>
T operator*(const Vecteur<T> &v, const Vecteur<T> &w);



Tout ça n'est pas cohérent. Pourquoi + est membre et pas * ?
En fait, aucun des deux ne devrait être membre.
Généralement, on met += comme opérateur membre, et + est une fonction
libre toute simple :

Vecteur<T> operator+ (const Vecteur<T> &v, const Vecteur<T> &w)
{
Vecteur<T> reponse (v);
reponse+= w;
return reponse;
}
Avatar
Fabien LE LEZ
On Mon, 15 Mar 2010 09:17:40 -0700 (PDT), Charly
:

pour les études, je dois réaliser une class Vecteur générique, voici
ce que j'ai fait pour l'instant:



Je n'ai pas l'énoncé sous les yeux, mais le design de la classe ne me
plaît pas.

Je préférerais un truc du style :

template <unsigned int dimension, class T>
class Vecteur
{
public:
Vecteur();

Vecteur <dimension, T> operator+= (Vecteur<dimension,T> const& v);

T &operator[](unsigned int i);
T operator[](unsigned int i) const;

private:
T m_values [dimension];
};

Le résultat est que "vecteur de float à 2 dimensions" et "vecteur de
float à 3 dimensions" sont deux types différents. Tenter d'ajouter un
vecteur 2D et un vecteur 3D donnera une erreur à la compilation.



Par ailleurs, même avec ton interface d'origine, tant que l'énoncé
n'indique rien, le moyen de stockage des données canonique est un
std::vector<>, pas un pointeur et de l'allocation dynamique.

template<class T>
class Vecteur
{
public:
Vecteur(unsigned int dim=3, const T &values=T());

Vecteur<T> &operator=(const Vecteur<T> &v);
Vecteur<T> operator+(const Vecteur<T> &v);
T &operator[](unsigned int i);
T const& operator[](unsigned int i) const;

unsigned int dimensions(void) const { return m_values.size(); }

private:
std::vector<T> m_values;
};

Enfin, note que "operator[] const" ne doit pas renvoyer une référence
non-const.
Avatar
Charly
Merci pour ces conseils qui me sont tres précieux.
J'ai effectué les modifs, voici le résultat:
Vecteur.h : http://pastebin.com/JdxLyVvb
Vecteur.cxx : http://pastebin.com/Ti8HA0GF

Je n'ai pas l' nonc sous les yeux, mais le design de la classe ne me
pla t pas.

Je pr f rerais un truc du style :

template <unsigned int dimension, class T>
class Vecteur
{
...
Par ailleurs, m me avec ton interface d'origine, tant que l' nonc
n'indique rien, le moyen de stockage des donn es canonique est un
std::vector<>, pas un pointeur et de l'allocation dynamique.



En fait je n'ai pas le choix, la première parte de l'exercice
consistait (entre autre) à comprendre comment fonctionne l'allocation
dynamique, la 2ème partie (ou je suis rendu) consiste à rendre le tout
générique, sinon j'aurai effectivement utilisé des std::vector, bien
plus simples.

Concernant la question de sujet dans mon premier message, comment puis-
je faire pour multiplier des float et des std::vector sans que le
compilateur ne râle ?

merci.
Avatar
Fabien LE LEZ
On Mon, 15 Mar 2010 15:10:19 -0700 (PDT), Charly
:

Concernant la question de sujet dans mon premier message, comment puis-
je faire pour multiplier des float et des std::vector sans que le
compilateur ne râle ?



Tel que je comprends l'énoncé, tu n'es pas censé pouvoir faire :

produit scalaire des vecteurs saisis (vecteur de « float » et de «
std ::string »). Normalement, vous devriez voir une erreur apparaître,



Mais tu dois expliquer pourquoi tu ne peux pas :

pourquoi ?



Et puisque ça ne fonctionne pas, tu dois virer la ligne en question
pour que le programme compile à nouveau :

Supprimez le traitement provoquant cette erreur.



Et enfin, tu dois en tirer une conclusion :

Que pouvez
vous déduire sur ce que fait le compilateur lorsque vous utilisez la
généricité ?"
Avatar
Fabien LE LEZ
On Mon, 15 Mar 2010 15:10:19 -0700 (PDT), Charly
:

http://pastebin.com/Ti8HA0GF

template<class T>
T operator*(const Vecteur<T> &v, const Vecteur<T> &w)
{
unsigned int dim = v.dimensions();



unsigned int const dim = v.dimensions();
Avatar
Fabien LE LEZ
On Mon, 15 Mar 2010 09:17:40 -0700 (PDT), Charly
:

"Modifiez les traitements effectués dans votre programme principal
afin d’afficher la valeur du
produit scalaire des vecteurs saisis (vecteur de « float » et de «
std ::string »)



Je suis à peu près sûr que ton prof ne s'attend pas à cette réponse,
mais amusons-nous tout de même :

Tu veux multiplier un Vecteur<truc> par un Vecteur<machin>.
Il faut donc définir cet opérateur :

template<class T, class U>
T operator*(const Vecteur<T> &v, const Vecteur<U> &w)
{
unsigned int const dim = v.dimensions();
assert( dim == w.dimensions() );

T r = 0;
for(unsigned int i=0; i<dim; ++i)
r+= Mult (v[i], w[i]);

return r;
}

Note le "Mult" à la place de "*". Ça nous permet de définir notre
propre multiplication. Dans le cas général, pas de surprise :

template<class T, class U>
T Mult (T t, U u)
{
return t*u;
}

Sauf que si U est std::string, ça ne fonctionne pas. Il faut donc un
cas spécial :

#include <sstream>

template<class T>
T Mult (T t, std::string s)
{
T u;
std::istringstream ifs (s);
ifs >> u;
return t*u;
}




Affichons maintenant, fièrement, le résultat :

int main()
{
Vecteur<float> vf (3, 1.2);
Vecteur<std::string> vs (3, "42.0");
std::cout << (vf * vs) << "n";
}
Avatar
Fabien LE LEZ
template<class T>
T operator*(const Vecteur<T> &v, const Vecteur<T> &w)
{
unsigned int dim = v.dimensions();
assert( dim == w.dimensions() );



Je préférerais :

template<class T>
T operator*(const Vecteur<T> &v, const Vecteur<T> &w)
{
assert( v.dimensions() == w.dimensions() );
unsigned int dim = v.dimensions();

D'une part, ça annonce dès le début les conditions requises ; d'autre
part, en cas de problème, le message d'erreur est plus clair,
puisqu'il fait intervenir les paramètres, pas les variables locales.
Avatar
Fabien LE LEZ
On Mon, 15 Mar 2010 15:10:19 -0700 (PDT), Charly
:

la première parte de l'exercice
consistait (entre autre) à comprendre comment fonctionne l'allocation
dynamique, la 2ème partie (ou je suis rendu) consiste à rendre le tout
générique,



En fait, tu aurais dû garder ton code séparé en deux parties :

- une classe qui ne s'occupe que de l'allocation dynamique (sorte de
version light de std::vector<>) ;
- une classe qui ne s'occupe que de maths.

Ça fait quelque peu de redites, mais ça permet de séparer les
responsabilités, et d'améliorer la réutilisabilité :
- tu pourras utiliser GestionMemoire<> pour autre chose que des
vecteurs ;
- tu pourras remplacer GestionMemoire<> par std::vector<> dans
Vecteur<>.



template <class T> class GestionMemoire
{
public:
GestionMemoire (unsigned int nb_elements, T const& modele);
~GestionMemoire();
GestionMemoire& operator= (GestionMemoire&);

T& operator[] (unsigned int);
T const& operator[] (unsigned int) const;
unsigned int size() const;

void swap (GestionMemoire<T>&);

private:
unsigned int nb_elements;
T* elements;
};

template template <class T> class Vecteur
{
public:
Vecteur (unsigned int nb_elements, T const& modele= T()) : data
(nb_elements, modele) {}

T& operator[] (unsigned int n) { return data[n]; }
T const& operator[] (unsigned int n) const { return data[n]; }
unsigned int size() const { return data.size(); }

T& operator += (GestionMemoire<T> const&);

private:
GestionMemoire<T> data;
};
Avatar
Michael Doubez
On 15 mar, 17:17, Charly wrote:
Bonjour à tous,
pour les études, je dois réaliser une class Vecteur générique, vo ici
ce que j'ai fait pour l'instant:
Vecteur.hhttp://pastebin.com/mSw1VpAL
Vecteur.cxxhttp://pastebin.com/2rVV3pGe



A noter que le corps des templates doit être inclus dans le code
source utilisateur.
Une solution est de tout mettre dans le .h.
Une autre est de créer un autre fichier (comme tu l'as fait) avec
l'extension .t/.tpp/.ipp (il n'y a pas d'extension reservée) et de
l'inclure dans le .h.

J'aimerai avec une (ou des) critiques sur mon code, ai-je pris des
mauvaises habitudes ? Y'a t'il des optimisation à faire ? ...



Pour faire l'opérateur d'addition, il est souvent plus aisé (et plus
idiomatique) de définir l'opérateur += dans la classe et de l'utilise r
dans la fonction exteene operator+()

class Vecteur
{
// ...
Vecteur& operator+=(const Vecteur &v)
{
// changer this
}
};

Vecteur operator+(const Vecteur &lhs, const Vecteur &rhs)
{
Vecteur tmp(lhs);
lhs+= rhs;
return tmp;
}

De plus, voici une question qui me pose bien des problèmes:
"Modifiez les traitements effectués dans votre programme principal
afin d’afficher la valeur du
produit scalaire des vecteurs saisis (vecteur de « float » et de   «
std ::string »). Normalement, vous devriez voir une erreur apparaître ,
pourquoi ? Supprimez le traitement provoquant cette erreur. Que pouvez
vous déduire sur ce que fait le compilateur lorsque vous utilisez la
généricité ?"

Je comprend bien qu'il n'est pas possible du multiplier des float et
des string mais je ne vois pas comment corriger cette erreur. Comment
faire ?



Ca dépends du contenu des string, si elles contiennent des valeurs
convertibles en entier/double ... Il est possible d'utiliser le
résultat de strtod() pour faire le produit scalaire.

--
Michael
1 2 3