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.

8 réponses

1 2 3
Avatar
Marc
[Note: je n'ai pas lu la conversation, je réagis juste à une ligne lue
par hasard]

Michael Doubez wrote:

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");
}



Un istream a des options spécifiant s'il doit lancer une exception ou pas
dans certaines situations (et par défaut, c'est non). Balancer une
exception de façon inconditionnelle ne suit pas vraiment la pratique
encouragée par le standard. Après, on fait ce qu'on veut, tant qu'on
documente...
Avatar
Fabien LE LEZ
On Tue, 23 Mar 2010 08:28:12 -0700 (PDT), James Kanze
:

dans un swap membre d'une classe vecteur, tu
n'échange que des pointeurs, qui est sans risque d'exception ;



Faire la même chose que swap() (i.e. échanger des pointeurs) dans le
corps de operator= ne change pas grand-chose au problème des
exceptions, ni à la vitesse.

Pour rappel, sa question était la différence entre

void C::swap (C& c)
{
std::swap (un_pointeur, c.un_pointeur);
}

C& operator= (C const& src)
{
C temp;
swap (temp);
return *this;
}

et

C& operator= (C const& src)
{
C temp;
std::swap (un_pointeur, c.un_pointeur);
return *this;
}
Avatar
James Kanze
On Mar 23, 5:09 pm, Fabien LE LEZ wrote:
On Tue, 23 Mar 2010 08:28:12 -0700 (PDT), James Kanze
:

>dans un swap membre d'une classe vecteur, tu n'échange que
>des pointeurs, qui est sans risque d'exception ;

Faire la même chose que swap() (i.e. échanger des pointeurs)
dans le corps de operator= ne change pas grand-chose au
problème des exceptions, ni à la vitesse.



Certes. Il y a même des solutions plus rapides. Moi-même, je ne
sais pas pourquoi on insiste tellement sur l'idiome de swap,
sinon que c'est devenu assez habituel d'utiliser swap dans
d'autres contextes aussi (comme pour libérer toute la mémoire
utilisée par un vector quand on le vide).

Pour rappel, sa question était la différence entre

void C::swap (C& c)
{
std::swap (un_pointeur, c.un_pointeur);
}

C& operator= (C const& src)
{
C temp;
swap (temp);
return *this;
}

et

C& operator= (C const& src)
{
C temp;
std::swap (un_pointeur, c.un_pointeur);
return *this;
}



Dans ce cas-là, il n'y en a pas. Sauf éventuellement qu'il
convient d'offrir aussi la fonction swap, ainsi qu'une
spécialisation de std::swap qui l'utilise, pour les autres
utilisateurs (par exemple, quelqu'un qui appelle sort sur un
vector de tes classes). Mais la solution classique était
plutôt :

C& C::operator=(C const& other)
{
T* p = new T(*other.un_pointeur);
delete un_pointer;
un_pointer = p;
return *this;
}

Ce qui marche aussi bien, et qui est même légèrement plus rapide
que la solution avec swap. Mais qui devient plus compliquer
quand on a plusieurs pointeurs. De même que si on a plusieurs
pointeurs, ne pas utiliser swap dans l'operator= implique la
duplication de la liste des appels à std::swap, une fois dans
l'operator=, une deuxième dans la fonction swap.

--
James Kanze
Avatar
Fabien LE LEZ
On Tue, 23 Mar 2010 14:20:03 -0700 (PDT), James Kanze
:

Moi-même, je ne
sais pas pourquoi on insiste tellement sur l'idiome de swap,



Parce que ça permet d'avoir du code simple et facile à mettre à jour :

- swap() contient une ligne par variable membre, et rien d'autre. Tu
ajoutes un membre => tu ajoutes une ligne dans swap().

- operator=() contient toujours le même code, à tel point qu'on n'a
pas besoin d'y réfléchir -- on se contente de le recopier.
Avatar
Fabien LE LEZ
On Tue, 23 Mar 2010 14:20:03 -0700 (PDT), James Kanze
:

Mais la solution classique était plutôt :



"Était", effectivement.

C& C::operator=(C const& other)
{
T* p = new T(*other.un_pointeur);
delete un_pointer;
un_pointer = p;



En gros, tu refais le boulot du constructeur et du destructeur.
C'est-à-dire que dès que le constructeur sera modifié, tu devras
modifier ta copie également. Pas glop.
D'ailleurs, note que tu as mal recopié le constructeur codé par l'OP,
puisque ledit constructeur allouait un tableau (new T[taille]).

L'important dans l'idiome "swap" est qu'on n'a pas à réfléchir -- on
utilise le code qui existe déjà dans le constructeur et le
destructeur.
Avatar
Michael Doubez
On 23 mar, 17:38, Marc wrote:
[Note: je n'ai pas lu la conversation, je réagis juste à une ligne lu e
par hasard]

Michael Doubez  wrote:
>  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") ;
> }

Un istream a des options spécifiant s'il doit lancer une exception ou p as
dans certaines situations (et par défaut, c'est non). Balancer une
exception de façon inconditionnelle ne suit pas vraiment la pratique
encouragée par le standard. Après, on fait ce qu'on veut, tant qu'on
documente...



Le code originel est du type:

void readValues()
{
for(int i = 0 ; i < x ; ++i)
{
int value;
in>>value;
}
}

Mon point est que le minimum de gestion d'erreur est de sortir avec un
état d'erreur.

--
Michael
Avatar
Marc
Michael Doubez wrote:

On 23 mar, 17:38, Marc wrote:
[Note: je n'ai pas lu la conversation, je réagis juste à une ligne lue
par hasard]

Michael Doubez  wrote:
>  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");
> }

Un istream a des options spécifiant s'il doit lancer une exception ou pas
dans certaines situations (et par défaut, c'est non). Balancer une
exception de façon inconditionnelle ne suit pas vraiment la pratique
encouragée par le standard. Après, on fait ce qu'on veut, tant qu'on
documente...



Le code originel est du type:

void readValues()
{
for(int i = 0 ; i < x ; ++i)
{
int value;
in>>value;
}
}

Mon point est que le minimum de gestion d'erreur est de sortir avec un
état d'erreur.



Et mon point est qu'un istream comme in contient déjà cet état d'erreur.
Après je veux bien qu'on puisse vouloir court-circuiter le reste de la
boucle dès qu'on rencontre une erreur.
Avatar
James Kanze
On 23 Mar, 22:05, Fabien LE LEZ wrote:
On Tue, 23 Mar 2010 14:20:03 -0700 (PDT), James Kanze
:

> Mais la solution classique était plutôt :

"Était", effectivement.

> C& C::operator=(C const& other)
> {
> T* p = new T(*other.un_pointeur);
> delete un_pointer;
> un_pointer = p;

En gros, tu refais le boulot du constructeur et du destructeur.
C'est-à-dire que dès que le constructeur sera modifié, tu devras
modifier ta copie également. Pas glop.



Il s'agit ici d'une classe simple, avec un seul pointeur. Pour
des classes plus complexe, évidemment, d'autres solutions
s'imposent. Je n'écarte pas l'idiome de swap ; je l'utilise
moi-même assez souvent. Mais pas comme automatisme : pour des
cas simples, c'est de l'overkill.

D'ailleurs, note que tu as mal recopié le constructeur codé
par l'OP, puisque ledit constructeur allouait un tableau (new
T[taille]).



Je me rappelle pas. Ce que j'ai présenté, c'est la solution plus
ou moins générique pour des classes à un seul pointeur (genre
idiome de pare-feu de compilation).

L'important dans l'idiome "swap" est qu'on n'a pas à réfléchir
-- on utilise le code qui existe déjà dans le constructeur et
le destructeur.



L'important, c'est de réfléchir de temps en temps:-).
Sérieusement, l'idiome de swap n'est pas mauvais, mais
l'opérateur d'affectation doit toujours être le sujet d'un peu
de réflection. Et qu'il y a des modèles de conception où
d'autres solutions sont aussi bien, voire mieux, et du moment
qu'on les utilise en tant que partie du modèle, je ne vois pas
le problème.

--
James Kanze
1 2 3