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
Fabien LE LEZ
On Sun, 11 Jun 2006 08:20:20 +0200, 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 ...


Ça se fait assez facilement en se basant sur un tableau de tableaux
(std::vector<std::vector<T> >, par exemple).


template <class T>
class Matrice
{
public:
typedef std::vector<T> Ligne;
typedef std::vector<Ligne> Data;
private:
Data data;
};

Il ne reste plus qu'à rajouter les fonctions qui vont bien.

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]


Note que tu peux faire un truc très basique avec

typedef std::vector<std::vector<Rectangle> > MatriceDeRectangles;

MatriceDeRectangles mon_pave (largeur, hauteur);

-> "mon_pave[i][j]" est alors correct, si i et j ont de bonnes
valeurs.

Note néanmoins que tu risques de vite trouver ça insuffisant.
Mieux vaut créer une classe "Matrice" propre.

Avatar
Sylvain
eb wrote on 11/06/2006 08:20:
Je cherche à créer une matrice à 2 dimensions d'une classe quelconque.


(disons T, la "classe quelconque")

une matrice de pointeurs d'instances ou une matrice d'instances ?

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


cela exclut un "T[R][K]" statique mais ne définit pas complètement le
comportement voulu:

- qui est responsable de l'allocation / destruction des éléments ?
si c'est la matrice, celle-ci peut stocker des références (un T[][]),
sinon elle définira surement un T*[][] afin de simplifier la gestion des
élements (pas indispensable si T est "léger", avec un opérateur de copie
implicite ou léger).

- la matrice doit-elle être redimensionable ?
si c'est le cas, il est également plus simple que chaque cellule soit un
T*, ceci permettra de copier les pointeurs dans une nouvelle matrice
redimensionnée, plutôt que de cloner les références stockées.

- est-ce que toutes les lignes ont la même taille ?
si c'est le cas, une approche T**[] est adaptée et minimisera les
overheads; si ce n'est pas le cas (la matrice est un vecteur de liste),
la solution de Fabien est plus adaptée.

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


supposons les lignes homogènes et les objets légers, une définition
possible est:

template <class T>
class Matrice {
private:
typedef T* Tptr;
T** items;
int rows, cols;

public:
Matrice(int row, int col){
cols = col;
items = new Tptr[rows = row];
for (register int i = 0; i < row; ++i)
items[i] = new T[col];
}
virtual ~Matrice(){
for (register int i = 0; i < rows; ++i){
if (items[i])
delete [] items[i];
}
delete [] items;
}
...
};

si la matrice contient des T*, on écrira:

template <class T>
class Matrice {
private:
typedef T* Tptr;
Tptr** items;
int rows, cols;

public:
Matrice(int row, int col){
cols = col;
items = new Tptr*[rows = row];
for (register int i = 0; i < row; ++i){
items[i] = new Tptr[col];
for (register int k = 0; k < col; ++k)
items[i][k] = null;
}
}
virtual ~Matrice(){
for (register int i = 0; i < rows; ++i){
if (items[i]){
for (register int k = 0; k < cols; ++k)
if (items[i][k])
delete items[i][k];
delete [] items[i];
}
}
delete [] items;
}
...
};

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]


l'opérateur [] peut ne pas être la meilleure solution.

avec une solution vecteur de list, c'est correct car MonPavé[i] retourne
un std::vector qui réalisera tous les contrôles requis pour la
résolution de [j]

si par contre, le pavé est un simple array (un T**{*}), [i] retournera
un T* (ou un T**) sur lequel plus aucun controle (de validité d'indice)
ne peut être appliqué, dans un tel cas, on pourra préférer la notation
T{*|&} t = MonPavé(row, line);

cas T[][]

T& operator() (int r, int k) const {
if (r >= rows) throw "error";
if (k >= cols) throw "error";
return items[r][k];
}

ou, cas T*[][]

T* operator() (int r, int k) const {
if (r >= rows) throw "error";
if (k >= cols) throw "error";
return items[r][k];
}

Avatar
eb
Merci pour ton post très complet.
N'étant pas programmeur, je crans cependant de m'y perdre.
Je vais essayer la proposition de Fabien dans un premier temps

Sylvain wrote:

eb wrote on 11/06/2006 08:20:
Je cherche à créer une matrice à 2 dimensions d'une classe quelconque.


(disons T, la "classe quelconque")

une matrice de pointeurs d'instances ou une matrice d'instances ?

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


cela exclut un "T[R][K]" statique mais ne définit pas complètement le
comportement voulu:

- qui est responsable de l'allocation / destruction des éléments ?
si c'est la matrice, celle-ci peut stocker des références (un T[][]),
sinon elle définira surement un T*[][] afin de simplifier la gestion des
élements (pas indispensable si T est "léger", avec un opérateur de copie
implicite ou léger).

- la matrice doit-elle être redimensionable ?
si c'est le cas, il est également plus simple que chaque cellule soit un
T*, ceci permettra de copier les pointeurs dans une nouvelle matrice
redimensionnée, plutôt que de cloner les références stockées.

- est-ce que toutes les lignes ont la même taille ?
si c'est le cas, une approche T**[] est adaptée et minimisera les
overheads; si ce n'est pas le cas (la matrice est un vecteur de liste),
la solution de Fabien est plus adaptée.

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


supposons les lignes homogènes et les objets légers, une définition
possible est:

template <class T>
class Matrice {
private:
typedef T* Tptr;
T** items;
int rows, cols;

public:
Matrice(int row, int col){
cols = col;
items = new Tptr[rows = row];
for (register int i = 0; i < row; ++i)
items[i] = new T[col];
}
virtual ~Matrice(){
for (register int i = 0; i < rows; ++i){
if (items[i])
delete [] items[i];
}
delete [] items;
}
...
};

si la matrice contient des T*, on écrira:

template <class T>
class Matrice {
private:
typedef T* Tptr;
Tptr** items;
int rows, cols;

public:
Matrice(int row, int col){
cols = col;
items = new Tptr*[rows = row];
for (register int i = 0; i < row; ++i){
items[i] = new Tptr[col];
for (register int k = 0; k < col; ++k)
items[i][k] = null;
}
}
virtual ~Matrice(){
for (register int i = 0; i < rows; ++i){
if (items[i]){
for (register int k = 0; k < cols; ++k)
if (items[i][k])
delete items[i][k];
delete [] items[i];
}
}
delete [] items;
}
...
};

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]


l'opérateur [] peut ne pas être la meilleure solution.

avec une solution vecteur de list, c'est correct car MonPavé[i] retourne
un std::vector qui réalisera tous les contrôles requis pour la
résolution de [j]

si par contre, le pavé est un simple array (un T**{*}), [i] retournera
un T* (ou un T**) sur lequel plus aucun controle (de validité d'indice)
ne peut être appliqué, dans un tel cas, on pourra préférer la notation
T{*|&} t = MonPavé(row, line);

cas T[][]

T& operator() (int r, int k) const {
if (r >= rows) throw "error";
if (k >= cols) throw "error";
return items[r][k];
}

ou, cas T*[][]

T* operator() (int r, int k) const {
if (r >= rows) throw "error";
if (k >= cols) throw "error";
return items[r][k];
}



Avatar
eb
Fabien LE LEZ wrote:

On Sun, 11 Jun 2006 08:20:20 +0200, 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 ...


Ça se fait assez facilement en se basant sur un tableau de tableaux
(std::vector<std::vector<T> >, par exemple).


template <class T>
class Matrice
{
public:
typedef std::vector<T> Ligne;
typedef std::vector<Ligne> Data;
private:
Data data;
};

Il ne reste plus qu'à rajouter les fonctions qui vont bien.

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]


Note que tu peux faire un truc très basique avec

typedef std::vector<std::vector<Rectangle> > MatriceDeRectangles;

MatriceDeRectangles mon_pave (largeur, hauteur);

-> "mon_pave[i][j]" est alors correct, si i et j ont de bonnes
valeurs.

Note néanmoins que tu risques de vite trouver ça insuffisant.
Mieux vaut créer une classe "Matrice" propre.



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

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

mais sans succès :

/home/eb/Packages/qgo.new/src/board.cpp: In member function `void
Board::drawGatter2()':

/home/eb/Packages/qgo.new/src/board.cpp:344: warning: unused variable 'cols'
/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include/g++-v3/bits/stl_vector.h: In
constructor `std::vector<_Tp, _Alloc>::vector(size_t) [with _Tp QCanvasLine, _Alloc = std::allocator<QCanvasLine>]':

/home/eb/Packages/qgo.new/src/board.cpp:345: instantiated from here

/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include/g++-v3/bits/stl_vector.h:207:
error: no matching function for call to `QCanvasLine::QCanvasLine()'

/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*)


Mais, si j'essaie un 'candidat'
(mettons QCanvasLine::QCanvasLine(canvas), où 'canvas' est déjà déclaré ),
ça ne va pas mieux :

/home/eb/Packages/qgo.new/src/board.cpp: In member function `void
Board::drawGatter2()':
/home/eb/Packages/qgo.new/src/board.cpp:341: error: conversion from
`QCanvasLine*' to non-scalar type `QCanvasLine' requested
/home/eb/Packages/qgo.new/src/board.cpp:345: error: `Board::canvas' cannot
appear in a constant-expression
/home/eb/Packages/qgo.new/src/board.cpp:345: error: template argument 1 is
invalid
/home/eb/Packages/qgo.new/src/board.cpp:345: error: template argument 2 is
invalid
/home/eb/Packages/qgo.new/src/board.cpp:345: error: invalid type in
declaration before '(' token


Une idée ?


Avatar
Fabien LE LEZ
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;

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


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


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


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

/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...

Avatar
Sylvain
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 ? ;)

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.

Sylvain.


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



/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).



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


Avatar
fabien.chene
Sylvain writes:

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


Pourquoi désuet ?

--
Fab

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

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.

--
Fab


Avatar
Sylvain
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 ?
(se référer au C n'est, par ailleurs, pas tjrs recevable)

Sylvain.


1 2 3