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 ?

10 réponses

1 2 3
Avatar
Sylvain
eb wrote on 11/06/2006 23:51:
Fabien LE LEZ wrote:


Ta classe "QCanvasLine" n'a pas de constructeur par défaut. Or, il en
faut un pour obtenir un vector<QCanvasLine>.


Ok, mais donc comment je fais ?


il faut définir un constructeur sans paramêtre, concrètement un:

QCanvasLine::QCanvasLine()
{
// initialisation des données membres
}

ceci est lié au fait que le template vector contient des références,
donc la déclaration:

std::vector< QCanvasLine > v(n);

construit n QCanvasLine et donc appelle n fois le constructeur de
QCanvasLine.

par analogie et de manière peut être plus évidente, la déclaration

Tptr aRow = new T[n];

construit (littéralement) également n instances de T via un appel à son
constructeur par défaut (celui sans paramètre).

selon les opérations faites sur les vector<> déclarés vous pourrez
également avoir besoin d'opérateurs de copie T& operator(const T&) d'un
constructor de recopie T(const T&).

ceci était dans ma première remarque ("matrice d'instance ou de
pointeurs") ... utiliser des classes templates toutes-faites est
pertinent si sans priver consiste à réinventer l'eau tiède, mais on ne
peux pas y recourir sans se poser les mêmes questions qu'imposerait un
codage original (pour l'usage s'entends, l'algorithmie interne est elle
épargnée).

Puisque justement quand j'utilise un constructeur, il refuse aussi ? (voir
fin de mon message précédent)


tu as listé un "QCanvasLine::QCanvasLine(canvas)", ce n'est pas un
constructeur sans paramètre.

c'est générique, il n'est pas possible d'invoquer un constructeur
paramétré pour un tableau d'instance, par exemple:

struct foo {
foo(int){}
};

foo f[3]; // erreur


soit les instances doivent être construites puis initialisées, ie:

struct foo {
foo(){}
void init(int) {}
};

foo f[3]; // invoque foo()
for (int i = 0; i<3; ++i)
f[i].init(anInt);

soit il faut gérer des pointeurs qui permettent de créer la matrice
(avec des cellules vides) dans un premier temps, puis les instances avec
paramètres ensuite, ie:

struct foo {
foo(int){}
};

foo** f = new foo*[3]; // (dommage que "foo* f[]" ne soit pas supporté)
for (int i = 0; i<3; ++i)
f[i] = new foo(i);

Sylvain.


Avatar
kanze
Sylvain wrote:
Fabien LE LEZ wrote on 11/06/2006 20:44:
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").


et pour "size" ou "length" on fait quoi ? ;)


taille, longueur ?

Dans la plupart de mes applications, j'ai été obligé à utiliser
des noms en anglais. En anglais, « number » est ambigu,
« numberOfRows » (ou number_of_rows) ne signifie pas la même
chose que « rowNumber ». Du coup, j'ai adopté la convention
« count », e.g. « rowCount ». En français, « nombre_de_lignes ».
Mais je conçois qu'on élide le « de », et qu'on abrège nombre en
nb.

En général, un nom tout court (comme « row » ou « ligne ») ne
doit être que le nom d'un type. En principe, parce que j'avoue
que c'est une règle que je ne respecte pas toujours. (La règle
générale, c'est que les noms de type sont des substantifs non
qualifiés, les noms de variables des substantifs qualifiés, et
les noms de fonction des verbes. Certains programmeurs le suit
prèsque réligieusement, et leur code est vraiment facile à lire
et à comprendre. D'autres, comme moi-même, le traite plutôt
comme une orientation générale, avec beaucoup d'exceptions ;
évidemment, on est convaincu que les exceptions qu'on fait
soi-même ne nuisent pas à la lisibilité (mais c'est un jugement
somme tout assez subjectif). Et d'autres (comme l'auteur de la
STL, apparamment), l'ignore complètement, au grand dam de leur
lecteurs.

Par ailleurs, il y a de fortes chances pour que ta variable
soit une constante :

static unsigned const nb_rows= 25;


voire même unsigned /int/, ce typage implicite est un héritage
désuet du C K&R.


Oui et non ? On peut le considérer des deux côtés. La sémantique
de unsigned est bien différente de celle d'int (et le C de K&R
permettait déjà aussi « unsigned int »). Et pourquoi dire
« unsigned int », et non « signed int » ?

Personellement, j'écrirais simplement « int » ; l'unsigned
signifie soit que je ne m'intéresse pas à la valeur numérique,
soit que je veux l'arithmétique modulo. Mais ce sont aussi des
conventions plus ou moins personels (mais assez répandues, quand
même).

--
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:
Fabien CHÊNE wrote on 12/06/2006 00:22:
Sylvain writes:

voire même unsigned /int/, ce typage implicite est un
héritage désuet du C K&R.


Pourquoi désuet ?


plus que pourquoi, dans quel cas ?

ici sur fclc++ proposer des expressions non typés, pour un
langage qu'il l'est fortement, est archaïque, non ?


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

Strictement parlant, la norme définit quatres catégories de
types de base entier, chacun avec des caractèristiques
différents : signed, unsigned, bool et caractère. Si on voulait
préciser tout en détail, il faudrait écrire :

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

Mais pour diverses raisons (largement historique, à vrai dire),
personne ne le fait -- sauf éventuellement pour char, parce que
« char » tout court, c'est un type caractère, c'est à dire un
type entier dont on ne précise pas s'il a la sémantique signed
ou la sémantique unsigned (mais qui malgré son nom n'a pas du
tout la sémantique d'un caractère).

Du coup, écrire « unsigned » à la place de « unsigned int »,
c'est un choix de style, pas plus désuet que d'écrire « int » à
la place de « signed int ». Les règles du codage doivent faire
un choix, et tout le monde y adhérer.

--
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
Fabien LE LEZ wrote:
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").

Par ailleurs, il y a de fortes chances pour que ta variable
soit une constante :

static unsigned const nb_rows= 25;


Si tu veux juste préciser une valeur numérique, j'aurais
tendance à préférer :

static int const rowCount = 25 ;

Si en revanche, tu veux t'aligner sur la STL (et vue que
l'utilistation, c'est bien comme paramètre de taille à la STL,
ce n'est pas une mauvaise idée), c'est :

static size_t const rowCount = 25 ;

Et vue qu'on est en train de chipoter pour des prunes, certains
préfèrent aussi :

static size_t const rowCount( 25 ) ;

Comme chacun sait, l'utilisation de = dans l'initialisation est
un C'isme désuet:-).

std::vector< QCanvasLine > v(rows) ;


Ceci est un tableau (à une dimension) d'objets QCanvasLine. Est-ce
bien ce que tu veux ?


Il faudrait voir la définition de QCanvasLine pour le savoir.

Si c'était un typedef à std::vector< Point >, évidemment, il
pourrait écrire :

std::vector< QCanvasLine > monPave( rowCount, columnCount ) ;

Au moins, c'est ce que dit la norme -- certains ont prétendu que
c'était une erreur dans la norme, et que ce n'était pas ce
qu'ils ont voulu dire. Et qu'il faudrait donc écrire :

std::vector< QCanvasLine > monPave( rowCount,
QCanvasLine( columnCount ) ) ;

error: no matching function for call to `QCanvasLine::QCanvasLine()'



Ce n'est donc pas un typedef à std::vector.

Ta classe "QCanvasLine" n'a pas de constructeur par défaut.
Or, il en faut un pour obtenir un vector<QCanvasLine>.


Pas du tout. Il faut un constructeur de copie, c'est tout. Il
ne faut un constructeur par défaut que si tu utilises le
paramètre par défaut.

/usr/qt/3/include/qcanvas.h:685: note: candidates are:
QCanvasLine::QCanvasLine(const QCanvasLine&)
/usr/qt/3/include/qcanvas.h:687: note: QCanvasLine::QCanvasLine(QCanvas*)


La liste des autres constructeurs (qui ne t'es guère utile).

Ce que g++ peut être verbeux...


N'est-ce pas ? Vive le bon vieux temps, où le compilateur
sortait simplement une "?" s'il y avait une erreur. Comme en
disait l'auteur, un programmeur expérimenté saurait de quoi il
s'agit.

--
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 11/06/2006 23:51:
Fabien LE LEZ wrote:

Ta classe "QCanvasLine" n'a pas de constructeur par défaut.
Or, il en faut un pour obtenir un vector<QCanvasLine>.


Ok, mais donc comment je fais ?


il faut définir un constructeur sans paramêtre, concrètement
un:

QCanvasLine::QCanvasLine()
{
// initialisation des données membres
}


Pas du tout. Il n'y a aucune exigeance qu'un type dans un
vecteur ait un constructeur par défaut -- tout ce qui est exigé,
c'est la copie et l'affectation.

Il ne nous a pas montré le code où il a essayé à initialiser.
C'est donc assez difficile à dire où se trouve l'erreur.
D'après les messages d'erreur, l'erreur se trouve dans une
fonction Board::drawGetter2(), à la ligne 341. Et qu'il n'a pas
peu convertir un QCanvasLine* en QCanvasLine, ce qui suggère
qu'il a passé un pointeur où il fallait un objet ; qu'en
l'invoquant avec « QCanvasLine( *canvas ) », peut-être.

Quant aux erreurs sur la ligne 345, j'ai l'impression qu'il a
essayé de passer l'objet comme un paramètre au template.

Mais sans voir les lignes en question, c'est impossible d'être
sûr.

ceci est lié au fait que le template vector contient des
références, donc la déclaration:

std::vector< QCanvasLine > v(n);

construit n QCanvasLine et donc appelle n fois le constructeur
de QCanvasLine.


n+1 fois, en fait. Cette ligne est l'équivalent de :

std::vector< QCanvasLine > v( n, QCanvasLine() ) ;

par analogie et de manière peut être plus évidente, la déclaration

Tptr aRow = new T[n];

construit (littéralement) également n instances de T via un
appel à son constructeur par défaut (celui sans paramètre).


Ce qui est radicalement différent de ce que fait std::vector,
qui lui n'utilise que le constructeur de copie.

selon les opérations faites sur les vector<> déclarés vous
pourrez également avoir besoin d'opérateurs de copie T&
operator(const T&) d'un constructor de recopie T(const T&).


Selon la norme, si tu ne les fournis pas, tu as un comportement
indéfini. Avec des versions les plus récentes de g++ (4.0+, je
crois), et avec les options qui conviennent, tu aurias bien une
erreur à la compilation si tu n'as pas de constructeur de copie
ou d'opérateur d'affectation accessible. Quoique tu fasse
d'autre. (Je dis bien accessible, parce qu'il est impossible
d'écrire une classe qui n'a pas de constructeur de copie ou
d'opérateur d'affectation. Tout au plus peut-on les rendre
privé.) Donc, par exemple, pour :

#include <vector>

class C {
C( C const& ) ;
C& operator=( C const& ) ;
public:
C() {}
} ;

void
f()
{
std::vector< C > v ;
}

j'ai :

Gabi (10): g++ --version
g++ (GCC) 4.0.2
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

Gabi (11): g++ -c -D_GLIBCXX_CONCEPT_CHECKS private.cc

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/boost_concept_check.h:
In member function 'void
__gnu_cxx::_SGIAssignableConcept<_Tp>::__constraints() [with _Tp = C]':

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/stl_vector.h:151:
instantiated from 'std::vector<C, std::allocator<C> >'
private.cc:14: instantiated from here
private.cc:5: error: 'C::C(const C&)' is private

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/boost_concept_check.h:208:
error: within this context
private.cc:6: error: 'C& C::operator=(const C&)' is private

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/boost_concept_check.h:209:
error: within this context

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/boost_concept_check.h:
In member function 'void
__gnu_cxx::_SGIAssignableConcept<_Tp>::__const_constraints(const _Tp&)
[with _Tp = C]':

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/boost_concept_check.h:210:
instantiated from 'void
__gnu_cxx::_SGIAssignableConcept<_Tp>::__constraints() [with _Tp = C]'

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/stl_vector.h:151:
instantiated from 'std::vector<C, std::allocator<C> >'
private.cc:14: instantiated from here
private.cc:5: error: 'C::C(const C&)' is private

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/boost_concept_check.h:213:
error: within this context
private.cc:6: error: 'C& C::operator=(const C&)' is private

/home/team02/jakan/gnu/gcc/install-4.0.2/lib/gcc/sparc-sun-solaris2.8/4.0.2 /../../../../include/c++/4.0.2/bits/boost_concept_check.h:214:
error: within this context

Si on travaille avec un g++ moderne, on doit utiliser au moins
les options suivantes :

-std=c++98
-pedantic
-D_GLIBCXX_CONCEPT_CHECKS
-D_GLIBCXX_DEBUG
-D_GLIBCXX_DEBUG_PEDANTIC

--
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
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. Et en français, nb
n'est pas du tout ambigu : c'est nombre, et non numéro. En
anglais, j'utilise le mot « count » dans ce cas-ci, pour éviter
l'ambiguïté. Ça me donne donc : « nbLignes » ou « lineCount ».

Même avec le s final, je trouve toujours cela ambigüe. nb_rows
pourrait vouloir dire numéro des lignes.

Je trouve qu'il est plus simple de nommer la variable
row_count.


En anglais. C'est la solution que j'ai adopté aussi. En
revanche, beaucoup d'anglophones se contentent de la distinction
« number[Of]Rows » et « rowNumber » -- l'ordre des mots enlève
l'ambiguité. J'ai aussi entendu parler des boîtes où la règle
interne été que number au sens de nombre se raccorcissait "num",
mais aus sens de numéro "no". (En fait, je crois que je n'ai
jamais vu l'abbreviation "no" en anglais sauf qu'avec la
signification numéro.)

L'allemand a la même distinction que le français, avec Anz
(=Anzahl) et Nr (=Nummer) qui correspondent extactement à notre
nb et no. Mais je me démande ce qu'on fait en Italie, où no
(=numero) correspond aussi bien à nb qu'à no, et l'ordre de mots
est celui du français, c-à-d qu'il ne permet pas de distinguer
non plus.

--
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
Fabien LE LEZ
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).

Avatar
fabien.chene
"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 ?

Et en français, nb n'est pas du tout ambigu : c'est nombre, et non
numéro. En anglais, j'utilise le mot « count » dans ce cas-ci, pour
éviter l'ambiguïté. Ça me donne donc : « nbLignes » ou « lineCount
».

Même avec le s final, je trouve toujours cela ambigüe. nb_rows
pourrait vouloir dire numéro des lignes.

Je trouve qu'il est plus simple de nommer la variable
row_count.


En anglais. C'est la solution que j'ai adopté aussi. En
revanche, beaucoup d'anglophones se contentent de la distinction
« number[Of]Rows » et « rowNumber » -- l'ordre des mots enlève
l'ambiguité. J'ai aussi entendu parler des boîtes où la règle
interne été que number au sens de nombre se raccorcissait "num",
mais aus sens de numéro "no". (En fait, je crois que je n'ai
jamais vu l'abbreviation "no" en anglais sauf qu'avec la
signification numéro.)


int noProblem;
int problemNumber;

;-)


--
Fab




Avatar
eb
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 ...

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

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 ?

Avatar
fabien.chene
Sylvain writes:

Fabien CHÊNE wrote on 12/06/2006 00:22:
Sylvain writes:

voire même unsigned /int/, ce typage implicite est un héritage désuet
du C K&R.
Pourquoi désuet ?


plus que pourquoi, dans quel cas ?


ici sur fclc++ proposer des expressions non typés, pour un langage
qu'il l'est fortement, est archaïque, non ?


J'aurais dis le contraire finalement, proposer des expressions typées
(voir la réponse de James) pour un langage qui n'est pas fortement
typé -- quoique cela dépend de la définition qu'on se donne -- est
futuriste :-)


--
Fab



1 2 3