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

Où est partie ma virtuelle ?

1 réponse
Avatar
diego-olivier.fernandez-pons
Bonjour,

J'=E9cris une biblioth=E8que de structures de donn=E9es adaptatives,
autrement dit on a une syntaxe de type

// enum type { Dense, Sparse, Fixed, Extensible }
Array t =3D makeArray (Sparse, size)
t=2EgetSize();

et la biblioth=E8que se charge d'utiliser une repr=E9sentation adapt=E9e
aux propri=E9t=E9s demand=E9es.

Pour ce faire, j'utilise une double indirection : pointeur + classe
abstraite.
Cela me permet :
- d'avoir des objets structur=E9s point=E9s (comme en Java)
- de pouvoir utiliser les classes concr=E8tes en cas d'imp=E9ratifs de
performance (ou test)
- de cacher toute la machinerie derri=E8re une interface simple

Le probl=E8me est que lorsque je renvoie des objets de type structur=E9s
d=E9pendants de l'impl=E9mentation concr=E8te (exemple : it=E9rateurs), le
type de l'objet semble perdu et le programme segfaute.

exemple :

int size =3D 5;

ArrayImplementation* ca =3D new ArrayImplementation (size); // classe
concrete
Array a =3D makeArray (Fixed, size); // redirig=E9 vers
ArrayImplementation

ca->getValue (1) // OK
a=2EgetValue (1) // OK

ArrayIterator cit (ca); // classe associ=E9e =E0 l'impl=E9mentation
concrete ArrayImplementation
cit.increment(); // OK

Iterator it =3D a.getIterator() // redirig=E9 vers ArrayIterator
it.increment(); // SEGFAULT

Valgrind incrimine l'indirection due pointeur dans la classe Iterator
class Iterator {
private:
AbstractIteratorI* ptr;
public:
Iterator (AbstractIterator* i) : ptr (i) {}
void increment() {ptr->increment() }; // use of unitialised value of
size 4
}

J'ai l'impression qu'il s'agit d'un probl=E8me de r=E9solution des
virtuelles en raison des multiples couches d'abstraction, mais j'ignore
comment y r=E9m=E9dier.

Voici un code r=E9duit montrant le probl=E8me :

class Iterator;

class AbstractIterator {
public:
virtual bool end () const =3D 0;
virtual int getValue () const =3D 0;
virtual void incr () =3D 0;
};

class AbstractArray {
public:
virtual int getValue (int) const =3D 0;
virtual void setValue (int, int) =3D 0;
virtual Iterator getIterator () =3D 0;
};

class Iterator {
private:
AbstractIterator* ptr;
public:
Iterator (AbstractIterator* i) : ptr (i) {}
bool end () const { return ptr->end(); }
int getValue () const { ptr->getValue(); }
void incr () { ptr->incr(); }
};

class Array {
private:
AbstractArray* ptr;
public:
Array (AbstractArray* a) : ptr (a) {}
int getValue (int i) const { ptr->getValue(i); }
void setValue (int i, int v) { ptr->setValue (i, v); }
Iterator getIterator () { ptr->getIterator(); }
};

class ArrayImplementation;

class ArrayIterator : public AbstractIterator {
private:
int* a;
int size;
int i;
public:
ArrayIterator (ArrayImplementation*);
bool end () const { return i < size; }
int getValue() const { return a[i]; }
void incr () { i++; }
};

class ArrayImplementation : public AbstractArray {
friend class ArrayIterator;
private:
int size;
int* a;
public:
ArrayImplementation (int siz) { a =3D new int [siz]; size =3D siz; }
int getValue (int i) const { return a[i]; }
void setValue (int i, int v) { a[i] =3D v; }
Iterator getIterator () {
ArrayIterator* it =3D new ArrayIterator (this);
return Iterator (it); }
};

ArrayIterator::ArrayIterator (ArrayImplementation* const imp) {
a =3D imp->a;
size =3D imp-> size;
i =3D 0;
}

enum array_type { Fixed, Extensible };

Array makeArray (int type, int size) {
if (type =3D=3D Fixed) {
ArrayImplementation* a =3D new ArrayImplementation (size);
return Array (a);
}
else {
assert (false);
}
}


int main (int argc, char** argv) {

int size =3D 5;

Array a =3D makeArray (Fixed, size);
ArrayImplementation* ca =3D new ArrayImplementation (size);

for (int i =3D 0; i < size; i++) {
a.setValue (i, i);
ca->setValue (i, i);
}

cout << "Array.getValue" << endl;
for (int i =3D 0; i < size; i++)
cout << a.getValue (i) << " ";
cout << endl;

cout << "ArrayImplementation*->getValue" << endl;
for (int i =3D 0; i < size; i++)
cout << ca->getValue (i) << " ";
cout << endl;

cout << "ArrayIterator" << endl;
for (ArrayIterator it (ca); it.end(); it.incr()) {
cout << it.getValue() << " ";
}
cout << endl;

cout << "Array.getIterator" << endl;
for (Iterator it =3D a.getIterator(); it.end(); it.incr()) {
cout << it.getValue() << " ";
}
=20
return 0;

}

Diego Olivier

1 réponse

Avatar
Pierre Barbier de Reuille
wrote:
Bonjour,

J'écris une bibliothèque de structures de données adaptatives,
autrement dit on a une syntaxe de type

// enum type { Dense, Sparse, Fixed, Extensible }
Array t = makeArray (Sparse, size)
t.getSize();

et la bibliothèque se charge d'utiliser une représentation adaptée
aux propriétés demandées.

Pour ce faire, j'utilise une double indirection : pointeur + classe
abstraite.
Cela me permet :
- d'avoir des objets structurés pointés (comme en Java)
- de pouvoir utiliser les classes concrètes en cas d'impératifs de
performance (ou test)
- de cacher toute la machinerie derrière une interface simple

Le problème est que lorsque je renvoie des objets de type structurés
dépendants de l'implémentation concrète (exemple : itérateurs), le
type de l'objet semble perdu et le programme segfaute.

[...]

J'ai l'impression qu'il s'agit d'un problème de résolution des
virtuelles en raison des multiples couches d'abstraction, mais j'ignore
comment y rémédier.

Voici un code réduit montrant le problème :

class Iterator;

class AbstractIterator {
public:
virtual bool end () const = 0;
virtual int getValue () const = 0;
virtual void incr () = 0;


manque le destructeur virtuel => problème lors de la désallocation

};

class AbstractArray {
public:
virtual int getValue (int) const = 0;
virtual void setValue (int, int) = 0;
virtual Iterator getIterator () = 0;
manque le destructeur virtuel => problème lors de la désallocation


};

class Iterator {
private:
AbstractIterator* ptr;
public:
Iterator (AbstractIterator* i) : ptr (i) {}
bool end () const { return ptr->end(); }


manque le return ...

int getValue () const { ptr->getValue(); }
là aussi


void incr () { ptr->incr(); }
};

class Array {
private:
AbstractArray* ptr;
public:
Array (AbstractArray* a) : ptr (a) {}
int getValue (int i) const { ptr->getValue(i); }
manque le return


void setValue (int i, int v) { ptr->setValue (i, v); }
Iterator getIterator () { ptr->getIterator(); }
ici aussi (d'où probablement la valeur non initialisée)


};
[...]


Diego Olivier



Bon, juste en corrigeant les "return" qui manquent et en rajoutant les
destructeurs virtuels ça passe chez moi !

Pierre