OVH Cloud OVH Cloud

Matrice (2 dimensions) de classe ?

29 réponses
Avatar
eb
Je cherche à créer une matrice à 2 dimensions d'une classe quelconque.
(mettons, des rectangles pour paver un plus grand carré)

En fait, ma matrice doit être de taille variable ...

Je ne suis pas arrivé à trouver la bonne déclaration dans .h, ni le bon code
dans .cpp

L'idée est de pouvoir dans le code, initialiser un pavage de la bonne
taille, puis d'accéder à mes éléments avec MonPavé[i][j]


Qqun a une idée ?

9 réponses

1 2 3
Avatar
James Kanze
Fabien CHÊNE wrote:
"kanze" writes:


Fabien CHÊNE wrote:
Fabien LE LEZ writes:
On Sun, 11 Jun 2006 20:35:49 +0200, eb :
unsigned rows = 25
Je n'aime pas beaucoup ce nom de variable : "rows" veut dire

"lignes", pas "nombre de lignes".
Généralement j'écris "nb_rows" (ou "nb_lignes").
Je trouve ce nom de variable assez fragile dans le sens où

il repose sur le s final. nb_row pourrait être interprété
comme numéro de la ligne (du fait que number veut dire à la
fois nombre et numéro).
Pas vraiement.




C'est vrai que le mélange d'anglais et de français peut souvent
prêter à la confusion. Mais ici, nb, c'est manifestement du
français -- ça ne signifie rien en anglais.



Je répondais par rapport au nom nb_rows :-p
nb n'est il jamais l'abbréviation de number en anglais ?


Il ne serait pas reconnu comme telle par un anglophone. J'en
sais quelque chose ; je m'en suis servi une fois par mégard,
dans un projet où travaillaient des anglais, et ils sont venu me
démander ce que ça signifiait.

--
James Kanze
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
James Kanze
Fabien LE LEZ wrote:
On Mon, 12 Jun 2006 22:47:44 +0200, (Fabien
CHÊNE):


nb n'est il jamais l'abbréviation de number en anglais ?



En pratique, il semble que "num" soit utilisé -- que ce soit
pour désigner un nombre (i.e. une taille) ou bien un numéro
(i.e. un indice).


Comme j'ai dit, no. est l'abbreviation « officielle », mais
seulement avec la signirifaction « numéro ». Num, c'est un
raccourci inventé des informaticiens, qui sert effectivement
pour les deux.

--
James Kanze
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
Sylvain
kanze wrote on 12/06/2006 09:47:

Mais ce n'est pas non typé ; « unsigned », c'est bien un type.


je comprends bien un "modifier" mais pas du tout un "type" !
peux-tu expliquer/détailler ? merci.

Strictement parlant, la norme définit quatres catégories de
types de base entier, ..

signed char unsigned char
signed short unsigned short
signed int unsigned int
signed long unsigned long


où hormis le cas char, la référence "int" est implicite lorsqu'un
modifier de taille existe (short, long), la liste non simplifiée étant
plutôt:

{signed | unsigned} {short | long } int

ma compréhension est que la désignation du type référé int peut être
omisse uniquement dans le cas où une spécification de taille existe (et
non dans le cas où seul un désignation de signe existe), n'est-ce pas le
cas?

(je reprécise le cadre est un "C++ un peu typé quand même")

Sylvain.

Avatar
Fabien LE LEZ
On Mon, 12 Jun 2006 23:31:28 +0200, eb :

Si je fais
{
QCanvasLine l = QCanvasLine::QCanvasLine(canvas);


Je ne saisis pas l'utilité du "QCanvasLine::".

...


Qu'y a-t-il dans ce "..." ?

l.show()
}


Avatar
Sylvain
eb wrote on 12/06/2006 23:31:

unsigned rows = 25
std::vector< QCanvasLine > v(rows, QCanvasLine::QCanvasLine(canvas) ) ;


j'ai été un peu surpris par cet équivalent fourni par James, j'y vois un
problème si les instances se recopient mal (oui elles sont "censées"
être copiable, mais l'erreur ...)

HS: comment est géré std::vector< T* > ???
comment est traiter un "redimensionne le vector à m éléments (m > n
initial), le T 'témoin' est-il gardé sous le coude en cas de besoin?

Comme v[i] n'est pas un pointeur, j'utilise v[i].show() (pour l'afficher) et
rien ...


avez-vous vérifié tous les données pointeurs de l'instance v[i] en cause?

l'opération de copie à "simplement" copié, si elles existent, ces
adresses pointeurs depuis la 'référence-témoin' passée à la construction
de la liste; si un/plusieurs pointeurs gérent un reference counting ou
vérifient leur consistence via à vis du QCanvasLine qui les utilisent
(ex; CanvasLine a été créé dans leur contexte, il est présent dans une
liste qu'ils maintiennent, etc).

ce point peut être invalidé ou cerné le problème en étudiant le
constructor QCanvasLine(const QCanvasLine&) ou en le fournissant s'il
n'existait pas.

Sylvain.

Avatar
kanze
Sylvain wrote:
kanze wrote on 12/06/2006 09:47:

Mais ce n'est pas non typé ; « unsigned », c'est bien un type.


je comprends bien un "modifier" mais pas du tout un "type" !
peux-tu expliquer/détailler ? merci.


Il y a une longue histoire derrière, et beaucoup de points de
vue sont possible. Selon la norme, les seuls « modifiers »
(« qualifiers », dans le langage de la norme) sont const et
volatile. (Mais j'ai du mal à considérer les « function
specifiers » autrement que comme « modifier » aussi.)

Ce qui est clair, c'est que « unsigned » et « unsigned int »
(et « int unsigned ») désigne le même type, et que c'est un
type différent de « int » ou « signed int ». Dans §3.9.1, la
norme liste les types entiers avec les noms « signed char »,
« short int », « int », « long int », puis « unsigned
char », « unsigned short int », « unsigned int », et
« unsigned long int » -- on pourrait donc prétendre que ces
noms-là sont les noms « canoniques ». Dans §7.1.5.2, en
revanche, il présente bien « unsigned » et « int » comme
étant des « simple type specifiers » tous les deux, sans la
moindre distinction entre eux.

Le résultat, c'est que les habitudes varient -- il y a beaucoup
d'exemples de « long », à la place de « long int », y
compris chez les meilleurs experts. On ne peut pas dire qu'une
utilisation est moins bien que l'autre. Moi aussi, je préfère
les noms canoniques, mais je m'adopte aux conventions d'où je
travaille à l'instant, et ça m'étonnerais que je sois
complètement cohérent dans le code que j'ai en ligne -- parce
que ce code a été en fait écrit sur un assez long période de
temps, et en partie pour des clients différents. Et il ne me
viendrait jamais à l'idée de dire à quelqu'un qu'il faut
« unsigned int » quand il a écrit simplement « unsigned ».

Note bien que je ne parle ici que les distinctions du genre
« unsigned » par rapport à « unsigned int », ou « long »
par rapport à « long int ». Des variants qui sont, en somme,
authorisés par l'histoire. Il y a d'autres cas, surtout en ce
qui concerne l'ordre, ou soit l'histoire, soit une logique
externe impose un variant précis. Donc, par exemple, si un
« storage class specifier » ou « typedef » est présent, il
doit être le premier, et « const » suit toujours ce qu'il
modifie. Je ne dis pas qu'il faut tolérer des choses du genre :
int const long extern unsigned x ;
même si c'est légal.

Strictement parlant, la norme définit quatres catégories de
types de base entier, ..

signed char unsigned char
signed short unsigned short
signed int unsigned int
signed long unsigned long


où hormis le cas char, la référence "int" est implicite
lorsqu'un modifier de taille existe (short, long),


C'est un point de vue. Je ne trouve rien dans la norme actuelle
pour le justifier. Il y a plusieurs façons de spécifier la
plupart des types, et la norme (dans §7.1.5.2) ne privilège
aucune. De point de vue de la sémantique, on pourrait très bien
prendre le point de vue que les types ci-dessus ont deux
sémantiques distinctes, signed et unsigned, et que chaque
sémantique existe en quatre tailles : char, short, int et long.
Ça ne reflête pas l'histoire, mais c'est une description assez
rationnelle de l'état actuel. Il n'y a plus de raison
fondamentelle à dire que dans « signed », il y a un « int »
implicit que de dire que dans « int », il y a un « signed »
implicit. En fait, si je ne régardais que la logique (à
l'exclusion de l'histoire et des habitudes établies), je dirais
que ce sont les formes ci-dessus qu'il faudrait préférées :
elle distinguent clairement les deux choses qui varient : la
sémantique (signed ou unsigned) et la taille.

la liste non simplifiée étant plutôt:

{signed | unsigned} {short | long } int

ma compréhension est que la désignation du type référé int
peut être omisse uniquement dans le cas où une spécification
de taille existe (et non dans le cas où seul un désignation de
signe existe), n'est-ce pas le cas?


Historiquement, ça représente à peu près l'évolution qui nous a
amené à la situation actuelle (beaucoup simplifié, évidemment --
signed est apparu assez tardivement, par exemple). Mais ça
néglige les types « signed char » et « unsigned char ». Et
surtout, ce n'est pas du tout ce que dit les normes (C++ et C).
Selon les normes (§6.7.3 de la norme C, §7.1.52. de la norme
C++), « int », « unsigned », « long », etc. sont tous des
« type specifiers ». Sans la moindre distinction entre eux.
Ensuite, il y a une liste exhaustive des combinaisons qui sont
permises, et leurs significations : dans la norme C++, on donne
pour chaque combinaison légale le nom canonique correspondant ;
dans la norme C, en revanche, on se contente de simplement
lister les combinaisons équivalentes sur la même ligne (avec
dans chaque cas la forme la plus courte première).

(je reprécise le cadre est un "C++ un peu typé quand même")


Justement. Si on considère du point de vue du système de type,
« unsigned » n'est pas qu'un « int » modifié, c'est un type
à part, avec une sémantique distincte d'« int » -- dans la
définition des opérateurs /, % et >>, dans son comportement en
cas de débordement, etc. Formellement, « short » est aussi un
type à part entier, et non un « int » modifié, mais les
différences en sont bien moindre.

--
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
eb wrote:
eb wrote:

J'ai essayé une version simplifiée dans un premier temps,

unsigned rows = 25
std::vector< QCanvasLine > v(rows) ;

mais sans succès :


Bon, j'ai progressé :
Le compilateur accepte :

unsigned rows = 25
std::vector< QCanvasLine > v(rows, QCanvasLine::QCanvasLine(canvas) ) ;

car QCanvasLine::QCanvasLine(canvas) est un constructeur valide.

et v[i] est valide.

Le seul problème, c'est que ça ne marche pas. Comme v[i]
n'est pas un pointeur, j'utilise v[i].show() (pour l'afficher)
et rien ...


Peut-être ton constructeur de copie ne fonctionne pas comme il
faut.

En fait, (mais c'est dû à on ignorance - je ne suis pas
programmeur) :


Je me démande si ça a un sens, alors. Il faut bien un minimum de
connaissance de la programmation pour pouvoir travailler en C++.

si je fais un code très simple

{
QCanvasLine *l = new QCanvasLine(canvas);
...
l->show()
}

ça marche

Si je fais
{
QCanvasLine l = QCanvasLine::QCanvasLine(canvas);
...
l.show()
}

ça marche pas (et pourtant, je vérifie bien que 'canvas' est
le parent de l ...)

Une idée ?


Ça a tout à fait l'air d'être un problème dans ton constructeur
de copie.

Je te conseille très fort d'acheter une copie de « Effective
C++ », de Scott Meyer, et de l'étudier soigneusement.

Pour l'instant, il faut que tu décides si QCanvasLine doit être
un type de valeur, ou un type d'entité. Dans le premier cas, tu
lui donne le constructeur de copie (et l'opérateur
d'affectation) qu'il faut, et tout doit marcher. Dans le
deuxième cas, tu les déclares privé (sans implémentation),
pour qu'on ne puisse pas servir par erreur. Et dans le deuxième
cas, tu ne peux pas les mettre dans une collection standard --
il faut plutôt une collection de pointeurs aux objets, et non
les objets mêmes.

--
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
Sylvain wrote:
eb wrote on 12/06/2006 23:31:

unsigned rows = 25
std::vector< QCanvasLine > v(rows, QCanvasLine::QCanvasLine(canvas) ) ;


j'ai été un peu surpris par cet équivalent fourni par James,
j'y vois un problème si les instances se recopient mal (oui
elles sont "censées" être copiable, mais l'erreur ...)


Si les objets ne sont pas copiables, on ne peut pas les mettre
dans une collection standard. Un point, c'est tout. La norme ne
permet pas à une implémentation de std::vector d'utilise le
constructeur par défaut -- il impose la copie, dans tous les
cas. Et si les objets ne sont pas copiables, le code ne se
compile pas -- au moins avec g++, avec les options pour avoir le
maximum de vérifications.

Ce que je crains, c'est que ses objets soient copiables, mais
que la copie est mal définie. Et là, c'est une question
fondamentale du C++ -- on n'écrit jamais de classe sans avoir
préalablement défini une politique vis-à-vis de la copie et de
l'affectation. Sinon, il n'y a pas que la STL qui risque de
poser de problèmes.

(Dans la norme, et en faisant abstraction des Allocator, le seul
constructeur de std::vector à un seul paramètre, c'est le
std::vector< T > v( n ) ;
le constructeur qui sert est celui déclaré :
std::vector<>::vector( size_t, T const& = T() ) ;
C'est donc qu'on lui passe bien un objet à copier, ici aussi.

D'après les messages d'erreur, j'ai l'impression que g++ a
utilisé deux constructeurs, à la place d'un paramètre par
défaut. Ce qui est explicitement permis, et qui n'est pas sans
avantages, précisement en ce qui concerne la qualité des
messages d'erreur.

HS: comment est géré std::vector< T* > ???
comment est traiter un "redimensionne le vector à m éléments
(m > n initial), le T 'témoin' est-il gardé sous le coude en
cas de besoin?


Non. Tu donnes toujours les éléments à y mettre. Dans le cas de
push_back ou insert, mais aussi dans le cas de resize, qui comme
le constructeur, prend deux paramètres, dont le deuxième a un
valeur par défaut.

Dans le cas des séquences (vector, deque, list), ces paramètres
par défaut sont les seules utilisations du constructeur par
défaut. Si tu passes toujours un paramètre explicit, sans
utiliser le défaut, le type n'a pas besoin de supporter la
construction par défaut. (Dans le cas des collections
associatives, genre map, l'opérateur [] utilise aussi le
constructeur par défaut du type mappé.)

--
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
Sylvain
kanze wrote on 13/06/2006 11:30:

Il y a une longue histoire derrière, et beaucoup de points de
vue sont possible. [...]


merci pour cette explication complète.
Sylvain.

1 2 3