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
Charly
Merci à tous pour le temps que vous m'avez accordé, voici les sources
finales de ma classe Vecteur
Vecteur.h : http://pastebin.com/LaxCLHRD
Vecteur.tpp : http://pastebin.com/Lw5mgbcU

Pour la conception, nous étions au départ très guidé par l'énonc é et
le but est d'apprendre le langage et ses mécanismes, je n'ai donc pas
trop eu le choix pour le design de la class.

J'aurais juste une dernière petite question, pour la surcharge de
operator=, j'ai créer une methode privée swap(const Vector<t> &v)
comme recommandé par Fabien Le Lez, quel est l'avantage par rapport à
l'utilisation directe de la fonction std::swap ?
Avatar
Fabien LE LEZ
On Mon, 22 Mar 2010 14:40:05 -0700 (PDT), Charly
:

j'ai créer



créé

une methode



Attention, le mot "méthode" est ambigu dans le contexte du C++. On lui
préférera l'expression "fonction membre".

(Le mot "méthode" peut sous-entendre "fonction membre virtuelle". Dans
un langage où toutes les fonctions membres sont virtuelles (ou au
moins virtuelles par défaut), il n'y a pas d'ambiguïté ; ce n'est pas
le cas en C++.)

privée



Non, publique.

swap(const Vector<t> &v)



Pas de const ici, puisque swap() modifie son argument.

quel est l'avantage par rapport à
l'utilisation directe de la fonction std::swap ?



Une fonction membre publique swap() s'avère souvent utile lors de
l'utilisation d'une classe. Puisque tu as besoin de la fonctionnalité,
autant l'implémenter.
Avatar
Charly Poyac
Ok un telle fonction peut s'avérer nécessaire dans une classe, mais
n'est ce pas un perte de ressource que de l'utiliser à la place de
std::swap (passer par un fonction membre utilisant elle même
std::swap, je n'en voit pas l'utilité) ?
Avatar
Fabien LE LEZ
On Mon, 22 Mar 2010 16:55:06 -0700 (PDT), Charly Poyac
:

mais n'est ce pas un perte de ressource



Qu'appelles-tu "perte de ressource" ?

que de l'utiliser à la place de std::swap



Une fonction membre swap() fait une seule chose : elle intervertit
chaque variable membre avec la variable membre correspondante dans
l'autre objet.
Ça signifie que le corps de cette fonction a exactement une ligne par
variable membre.
Ainsi, il est très facile de vérifier que tu n'en as pas oublié.

En revanche, operator= fait plusieurs choses :
- créer un objet temporaire
- effectuer le swap
- retourner *this

L'oubli d'une variable membre serait donc plus difficile à repérer.

D'une manière générale, il ne faut pas hésiter à séparer les
responsabilités. Sur le même principe, et comme je l'ai déjà dit, il
aurait fallu séparer les deux responsabilités de ta classe (Gestion
mémoire / Opérations mathématiques) en deux classes distinctes, si ton
prof l'avait autorisé.
Avatar
Fabien LE LEZ
J'ai écrit :

L'oubli d'une variable membre serait donc plus difficile à repérer.



...et d'ailleurs, ça n'a pas manqué. Tu as effectivement oublié un
membre.
Avatar
Fabien LE LEZ
...et d'ailleurs, ça n'a pas manqué



Désolé, j'avais regardé une version précédente.
Avatar
Fabien LE LEZ
On Mon, 22 Mar 2010 14:40:05 -0700 (PDT), Charly
:

Vecteur.h : http://pastebin.com/LaxCLHRD
Vecteur.tpp : http://pastebin.com/Lw5mgbcU

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



À la réflexion, ceci est incorrect. Il n'y a aucune raison d'interdire
d'assigner un vecteur 2D à un Vecteur<> qui contenait auparavant un
vecteur 3D.

template<class T>
void Vecteur<T>::swap(Vecteur<T>& autre)
{
std::swap (autre.m_dim, m_dim);
std::swap (autre.m_values, m_values);
}
Avatar
Fabien LE LEZ
On Mon, 22 Mar 2010 14:40:05 -0700 (PDT), Charly
:

Vecteur.h : http://pastebin.com/LaxCLHRD
Vecteur.tpp : http://pastebin.com/Lw5mgbcU

template<class T>
std::istream &operator>>(std::istream &in, Vecteur<T> &v)
{
unsigned int const dim = v.dimensions();
T t[dim];



Ceci est incorrect, à moins que cette fonctionnalité ait été ajoutée
au C++ récemment. En revanche, c'est correct en C, ce qui explique que
ton compilo ne bronche pas.
En C++, cette syntaxe n'est acceptable que si "dim" est connu à la
compilation.

En prime, cette variable ne sert à rien. Et "dim" ne sert pas
forcément à grand-chose non plus.

template<class T>
std::istream &operator>>(std::istream &in, Vecteur<T> &v)
{
for(unsigned int i=0; i<v.dimensions(); ++i)
{
in >> v[i];
}
return in;
}

Ou bien, si tu tiens à avoir une variable temporaire :

template<class T>
std::istream &operator>>(std::istream &in, Vecteur<T> &v)
{
unsigned int const dim = v.dimensions();
for(unsigned int i=0; i<dim; ++i)
{
T t;
in >> t;
v[i] = t;
}
return in;
}

(Au fait, est-ce que cette fonction fonctionne ? N'utilisant quasiment
jamais >>, j'ai comme un doute.)
Avatar
Michael Doubez
On 22 mar, 22:40, Charly wrote:
Merci à tous pour le temps que vous m'avez accordé, voici les sources
finales de ma classe Vecteur
Vecteur.h :http://pastebin.com/LaxCLHRD
Vecteur.tpp :http://pastebin.com/Lw5mgbcU



Ca m'a l'air pas mal.

Trois points:
1. il est possible d'utiliser des algorithms pour les fonctions
courantes:

std::fill(m_values,m_values+m_dim,values);
au lieu de
for(unsigned int i=0; i<m_dim; ++i)m_values[i] = values;

std::copy( m_values,m_values+m_dim, v.m_values);
au lieu de
for(unsigned int i=0; i<m_dim; ++i) m_values[i] = v.m_values[i];

...

C'est pas obligatoire mais c'est bien quand c'est plus clair et puis
tu provide des optimisations de l'implémentation

2. Dans operator<<(), dim peut être 0 et donc tu aura une erreur avec
ta boucle (dim-1=UINT_MAX).

3. Dans operator>>(), tu ne testes pas le resultat de >>

Un minimum serait:
if( !(in >> t[i]) )
{
std::throw std::runtime_error("could not read Vecteur from stream");
}

Pour la conception, nous étions au départ très guidé par l'énon cé et
le but est d'apprendre le langage et ses mécanismes, je n'ai donc pas
trop eu le choix pour le design de la class.

J'aurais juste une dernière petite question, pour la surcharge de
operator=, j'ai créer une methode privée swap(const Vector<t> &v)



Tu veux dire swap(Vector<t> &v)

comme recommandé par Fabien Le Lez, quel est l'avantage par rapport à
l'utilisation directe de la fonction std::swap ?



Tu peux surcharger std::swap() pour que ta fonction de swap soit
appelée en cas de:
Vecteur v1,v2;

std::swap(v1,v2);

Note: La contrainte de ne swapper que des Vecteurs de même taille me
parait forte.

--
Michael
Avatar
James Kanze
On 22 Mar, 21:40, Charly wrote:
Merci à tous pour le temps que vous m'avez accordé, voici les sources
finales de ma classe Vecteur
Vecteur.h :http://pastebin.com/LaxCLHRD
Vecteur.tpp :http://pastebin.com/Lw5mgbcU

Pour la conception, nous étions au départ très guidé par
l'énoncé et le but est d'apprendre le langage et ses
mécanismes, je n'ai donc pas trop eu le choix pour le design
de la class.

J'aurais juste une dernière petite question, pour la surcharge
de operator=, j'ai créer une methode privée swap(const
Vector<t> &v) comme recommandé par Fabien Le Lez, quel est
l'avantage par rapport à l'utilisation directe de la fonction
std::swap ?



Comme les autres te l'ont dit, il faut que le paramètre de swap
ne soit pas const, puisque tu vas le modifier. Quant à
l'intérêt : dans un swap membre d'une classe vecteur, tu
n'échange que des pointeurs, qui est sans risque d'exception ;
la fonction std::swap va faire quelque chose du genre :

template<typename T>
void
swap(T& l, T& r)
{
T t(l);
l = r;
r = t;
}

Avec un appel au constructeur de copie (qui lui pourrait lever
une exception, parce qu'il fait une copie profonde, avec
allocation d'un nouveau buffer, etc.) et deux appels à
l'opérateur d'affectation (ce qui interdit son utilisation dans
l'opérateur d'affectation, au moins d'aimer les récursions à
l'infini).

L'implémentation member de swap pourrait, évidemment, utiliser
std::swap sur les membres de la classe. Sans risque d'exception,
d'ailleurs, si ces éléments ont des constructeurs de copie et
des opérateurs d'affectation qui garantissent de ne pas lever
une exception (ce qui est le cas des types pointeur et des types
entiers).

En plus de garantir qu'il n'y aura pas d'exceptions, et de
pouvoir servir dans l'opérateur d'affectation, la fonction
membre a l'avantage d'être énormement plus rapide. idéalement,
si tu définis un membre swap, qui ne travaille que sur des
pointeurs, il serait bien aussi de définir une spécialisation de
std::swap qui appelle cette fonction membre. (Mais ce n'est pas
possible dans le cas d'un template, au mois officiellement.)

--
James Kanze
1 2 3