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

iterator = NULL

58 réponses
Avatar
Henri de Solages
Bonjour.

Est-il légal d'assigner la valeur NULL à un itérateur ?

--
Get rid of the final underscore of my address to use it.

10 réponses

1 2 3 4 5
Avatar
Henri de Solages
Je remarque cependant que ni c++ ni icpc (compilateur d'Intel) ne me jettent quand
j'affecte NULL à un itérateur.



Amusant: tu as un bout de code.


# include <iostream>
# include <string>
# include <list>

using namespace std ;

int main( int argc, char ** argv ) {
list< string >::const_iterator i_nombres = NULL ;

if( i_nombres != NULL )
cerr << "i_nombresn" ;
else
cerr << "non i_nombresn" ;
return 0 ;
}



--
Get rid of the final underscore of my address to use it.


Avatar
kanze
Marc Boyer wrote:
Loïc Joly writes:



| > Est-il légal d'assigner la valeur NULL à un itérateur ?

| Souvent, l'équivalent moral de NULL pour un itérateur sur
| une collection "c" est c.end()

Cela peut cependant réserver des surprises.

list<int> l;
// ...
copy(l.begin(), l.end(), back_inserter(l));


C'est marrant comme quoi des reflexes du C peuvent
rester incrusté...
Pour moi, end() dans une liste, c'est fondamentalement
le pointeur NULL de toutes les listes chainées C.


Depuis quand est-ce qu'il y a un pointeur NULL dans une liste
chaînée en C. J'ai toujours utilisé des listes doublement
chaînées (qui permettent de supprimer un élément en ne
connaissant que cet élément), avec une vertice « racine ». La
fin de la liste avec la racine comme prochain élément, et le
début comme élément précédant. Dans le cas d'une liste vide, la
racine pointait à lui-même.

Itérer sur tous les éléments devenait :

for ( Node* p = racine->next ; p != &racine, p = p->next ) ...

Supprimer un élément :

elem->next->prev = elem->prev ;
elem->prev->next = elem->next ;

L'utilisation des NULL ne fait que compliquer le code en
introduisant des tests supplémentaires.

Et à chaque fois, faut que je me raisonne en me disant
"mais non, comment faire end()-1 avec NULL ?".
Mais c'est *le* piège (1) ou je tombe systématiquement.

D'ailleurs, un rapide coup d'oeuil sur le code de la list de
mon g++ me dit que end() retourne un "_M_node", que begin()
retourne un "_M_node->_M_next", et la définition de _M_node
semble perdue deux niveaux d'héritage au dessus... Pas tout
clair pour moi.
Ils font une liste circulaire ?


Je crois. Je crois qu'il font à peu près comme j'ai fait
ci-dessus. J'ai l'impression que la seule différence entre leur
code et ce que je faisais auparavent (depuis au moins vingt ans
maintenant), c'est qu'ils allouent la racine dynamiquement,
tandis que chez moi, elle était membre. (Le membre, en fait,
c'était un « Node » qui ne contenait que des pointeurs. Les
éléments était des Element qui dérivait de Node, et je me
servais des casts dans la classe de liste pour passer d'un à
l'autre.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
korchkidu wrote:
Gabriel Dos Reis wrote:
Loïc Joly writes:



| > Est-il légal d'assigner la valeur NULL à un itérateur ?

| Souvent, l'équivalent moral de NULL pour un itérateur sur une
| collection "c" est c.end()

Cela peut cependant réserver des surprises.

list<int> l;
// ...
copy(l.begin(), l.end(), back_inserter(l));


JE vois pas le piege la. Tu peux expliquer stp?


Essai-le. Et demande-toi ce que bien signifie le résultat d'un
l.end() précédant après une insertion ?

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Henri de Solages

Pourquoi ne pas placer ces itérateurs dans un vector ?


Je n'aime pas les vector car, quand on fait un erase, les iterateurs sur les élements
suivants ne sont plus valides. J'aime bien qu'on fasse suivre le courrier quand on déménage.

--
Get rid of the final underscore of my address to use it.

Avatar
Henri de Solages
Je veux un tableau d'itérateurs sur des éléments d'une list.


Sinon, ce dont tu as besoin, c'est d'un délimiteur de
fin, donc le end() de la liste me semble une bonne
valeur interdite.


Mais si j'ajoute ensuite des éléments à ma liste, le end() de la list est-il tenu par la
norme de garder une même valeur ?


--
Get rid of the final underscore of my address to use it.


Avatar
Marc Boyer
Henri de Solages a écrit :
Je remarque cependant que ni c++ ni icpc (compilateur d'Intel) ne me jettent quand
j'affecte NULL à un itérateur.


Amusant: tu as un bout de code.


# include <iostream>
# include <string>
# include <list>

using namespace std ;

int main( int argc, char ** argv ) {
list< string >::const_iterator i_nombres = NULL ;

if( i_nombres != NULL )
cerr << "i_nombresn" ;
else
cerr << "non i_nombresn" ;
return 0 ;
}


En effet, compile sans rien dire avec mon g++ (gcc 3.3.5).
Je suis étonné.
En fait, si j'arrive à bien interpréter ce qui se passe
d'après un coup d'oeil au code de list sur ma machine,
il se passe qu'un itérateur est une classe dont le seul
membre est un pointeur sur un _List_node_base qui est
une classique cellule: PtrSuivant, PtrPrecedent, Valeur.

Hors, cette classe possède un constructeur qui prend
en paramètre un pointeur sur un _List_node_base.
Ce constructeur n'est pas "explicit" (ce qui me semble
une erreur de conception de prime abord), donc, si je
me plante pas, il introduit une conversion implicite
de _List_node_base* en iterator. Et comme NULL est
un _List_node_base*...

D'où deux questions:
- pour les gens avec un GCC plus récent, est-ce que
le pb existe encore ?
- pour les experts, est-ce qu'il ne faut pas considérer
l'absence d'explicit comme un bug à rapporter à
l'équipe de GCC ?

Marc Boyer
--
À vélo, prendre une rue à contre-sens est moins dangeureux
que prendre un boulevard dans le sens légal. À qui la faute ?



Avatar
Marc Boyer
Henri de Solages a écrit :
Je veux un tableau d'itérateurs sur des éléments d'une list.


Sinon, ce dont tu as besoin, c'est d'un délimiteur de
fin, donc le end() de la liste me semble une bonne
valeur interdite.


Mais si j'ajoute ensuite des éléments à ma liste, le end() de la list est-il tenu par la
norme de garder une même valeur ?


Comme souligné par Gaby, c'est loin d'être sur.
En fait, je ne sais pas, mais par expérience des questions
de Gaby, je pense que la répons est non (bien sur,
si la validité de mon code dépendait de ça, je ne
me baserait pas que sur cette intution).

Marc Boyer
--
À vélo, prendre une rue à contre-sens est moins dangeureux
que prendre un boulevard dans le sens légal. À qui la faute ?



Avatar
Marc Boyer
Le 08-09-2005, kanze a écrit :
Marc Boyer wrote:
Loïc Joly writes:




| > Est-il légal d'assigner la valeur NULL à un itérateur ?

| Souvent, l'équivalent moral de NULL pour un itérateur sur
| une collection "c" est c.end()

Cela peut cependant réserver des surprises.

list<int> l;
// ...
copy(l.begin(), l.end(), back_inserter(l));


C'est marrant comme quoi des reflexes du C peuvent
rester incrusté...
Pour moi, end() dans une liste, c'est fondamentalement
le pointeur NULL de toutes les listes chainées C.


Depuis quand est-ce qu'il y a un pointeur NULL dans une liste
chaînée en C.


A l'école ?

J'ai toujours utilisé des listes doublement
chaînées (qui permettent de supprimer un élément en ne
connaissant que cet élément), avec une vertice « racine ». La
fin de la liste avec la racine comme prochain élément, et le
début comme élément précédant. Dans le cas d'une liste vide, la
racine pointait à lui-même.


OK

Itérer sur tous les éléments devenait :

for ( Node* p = racine->next ; p != &racine, p = p->next ) ...

Supprimer un élément :

elem->next->prev = elem->prev ;
elem->prev->next = elem->next ;

L'utilisation des NULL ne fait que compliquer le code en
introduisant des tests supplémentaires.


Oui.

Et à chaque fois, faut que je me raisonne en me disant
"mais non, comment faire end()-1 avec NULL ?".
Mais c'est *le* piège (1) ou je tombe systématiquement.

D'ailleurs, un rapide coup d'oeuil sur le code de la list de
mon g++ me dit que end() retourne un "_M_node", que begin()
retourne un "_M_node->_M_next", et la définition de _M_node
semble perdue deux niveaux d'héritage au dessus... Pas tout
clair pour moi.
Ils font une liste circulaire ?


Je crois. Je crois qu'il font à peu près comme j'ai fait
ci-dessus. J'ai l'impression que la seule différence entre leur
code et ce que je faisais auparavent (depuis au moins vingt ans
maintenant), c'est qu'ils allouent la racine dynamiquement,


Oui. La classe possède un champ _M_node, qui semble être
cette racine (d'ailleurs, end() se contente de retourner
_M_node et begin() retourne _M_node->next), et le constructeur
fait
_M_node= _M_get_node();
qui est un appel à l'allocateur.

tandis que chez moi, elle était membre. (Le membre, en fait,
c'était un « Node » qui ne contenait que des pointeurs. Les
éléments était des Element qui dérivait de Node, et je me
servais des casts dans la classe de liste pour passer d'un à
l'autre.)


Oui.

Marc Boyer
--
À vélo, prendre une rue à contre-sens est moins dangeureux
que prendre un boulevard dans le sens légal. À qui la faute ?



Avatar
Stan
"Henri de Solages" a écrit dans le message de news:
dfospn$2l5$

Pourquoi ne pas placer ces itérateurs dans un vector ?


Je n'aime pas les vector car, quand on fait un erase, les iterateurs sur
les élements suivants ne sont plus valides. J'aime bien qu'on fasse suivre
le courrier quand on déménage.




Il était question de parcourir :

Pour parcourir mes tableaux de pointeurs facilement


Après, tout dépend des besoins réels, peut être qu'un set pourait
être une solution aussi.


--
-Stan


Avatar
Henri de Solages
Si tu as un tableau, pourquoi voudrais-tu le terminer par par NULL ?
Connaître la taille du tableau ne suffit pas pour savoir quand
s'arrêter ?


C'est bien ce que je pensais faire pour le cas avéré où l'on n'a pas le droit d'utiliser NULL,
mais c'est plus lourd : quand je passe le tableau à une fonction, il faut aussi lui passer
sa taille.


--
Get rid of the final underscore of my address to use it.

1 2 3 4 5