OVH Cloud OVH Cloud

realloc et templates

8 réponses
Avatar
Cyrille \cns\ Szymanski
Bonjour,

Je voudrais créer un tableau d'objets (définis dans une template) de
taille variable. Il n'y a pas d'opérateur renew[], je me suis donc
rabattu sur realloc() et c'est ce qui est délicat.

Le problème est d'appeler correctement les constructeurs. Bizarrement
(pour moi), le code fonctionne pour les destructeurs mais pas les
constructeurs.

Voici une partie du code :

template <class Obj>
struct cnsBuffer
{
Obj *m_ptr; // le tableau d'objets
unsigned int m_nb; // la taille du tableau

int allocate( unsigned int nb )
{
FATAL( nb!=0 );

int ret=-1;

if( nb<m_nb )
{
// détruire les objets supprimés
for( unsigned int i=nb; i<m_nb; ++i )
{
// ce bout de code fonctionne très bien
m_ptr[i].~Obj();
}
}

m_ptr = (Obj*)realloc( m_ptr, nb*sizeof(Obj) );
if( m_ptr!=0 )
{
// initialiser les nouveaux objets
for( unsigned int i=m_nb; i<nb; ++i )
{
// celui là pose problème
m_ptr[i].Obj();
}
m_nb = nb;
ret = 0;
}
else
{
m_nb = 0;
ret = -1;
}

return ret;
}

};


Avec Borland C++ 5.5 j'obtiens l'erreur que dans l'expression "m_ptr
[i].Obj();" :

Structure required on left side of . or .* in function cnsBuffer
<char>::allocate(unsigned int)
-> évidemment avec un char cela n'a pas de sens

'Obj' is not a member of 'TestClass' in function cnsBuffer
<TestClass>::allocate(unsigned int)
-> cette erreur me laisse perplexe, pourquoi le mécanisme de template ne
l'a pas traduit en TestClass() ?


En fait ma question est comment faire (proprement) ce que je cherche à
faire ?


J'ai aussi pensé à allouer un tableau temporaire avec new[] (dans le cas
où la taille augmente) puis le recopier dans la zone allouée par realloc
() et enfin le libérer avec free() pour ne pas appeler de destructeur,
mais c'est plutôt moche.


Merci d'avance pour votre aide,
--
_|_|_| CnS
_|_| for(n=0;b;n++)
_| b&=b-1; /*pp.47 K&R*/

8 réponses

Avatar
Fabien LE LEZ
On 18 Aug 2003 12:52:11 GMT, "Cyrille "cns" Szymanski"
wrote:

D'après ce que j'ai compris, il faut aussi doter la classe Obj de
l'opérateur
void *operator new(size_t s, Obj *a)


Ah ? T'es sûr ? Va falloir que je vérifie, mais je ne vois pas
pourquoi ça serait obligatoire.

Franchement je ne vois pas pourquoi "m_ptr[i].~Obj();" fonctionne et pas
"m_ptr[i].Obj();".


"m_ptr[i].~Obj();" a un sens, puisque m_ptr[i] est un objet de classe
Obj, construit précédemment.
Par contre, quand tu écris "m_ptr[i].Obj();", m_ptr[i] n'est pas
encore un objet de classe Obj -- je vois mal comment ça pourrait avoir
un sens. Mais bon, a priori je me trompe quelque part -- je n'ai
jamais réussi à comprendre le fonctionnement des appels "directs" aux
constructeurs/destructeurs.


--
Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/
et http://www.aminautes.org/forums/serveurs/tablefr.html
Archives : http://groups.google.com/advanced_group_search
http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html

Avatar
Fabien LE LEZ
On 18 Aug 2003 13:40:08 GMT, "Cyrille "cns" Szymanski"
wrote:

Oh, je dis ça parce que dans l'exemple suivant le compilateur


Quel compilateur utilises-tu ?
A noter que certains "vieux" compilos gèrent assez mal le "placement
new"...

void main( void )


int main()


--
Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/
et http://www.aminautes.org/forums/serveurs/tablefr.html
Archives : http://groups.google.com/advanced_group_search
http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html

Avatar
Patrick Mézard
Je voudrais créer un tableau d'objets (définis dans une template) de
taille variable. Il n'y a pas d'opérateur renew[], je me suis donc
rabattu sur realloc() et c'est ce qui est délicat.

Le problème est d'appeler correctement les constructeurs. Bizarrement
(pour moi), le code fonctionne pour les destructeurs mais pas les
constructeurs.


Tu peux jeter un coup d'oeil aux fonctions template std::unitialized_fill et
std::uninitialized_fill_n qui sont déclarées dans <memory>. A priori, ça
fait exactement ce que tu veux. Un peu de documentation à ce sujet :
http://www.sgi.com/tech/stl/uninitialized_fill.html
http://www.sgi.com/tech/stl/uninitialized_fill_n.html

Peut-être que l'implémentation fournie par Borland résoudra tes problèmes.
En plus, ça donne de bonnes garanties en cas de lancé d'exception lors de la
construction. Ce qui est un peu bizarre c'est qu'ils ne fournissent pas des
algorithmes similaires pour appeler les destructeurs.

Patrick Mézard

Avatar
Patrick Mézard
Merci beaucoup pour ces infos. J'ai regardé l'implémentation de SGI et
j'en ai extrait quelques bouts intéressants. La doc SGI donne un exemple
d'utilisation de construct() qui est la base de cet algorithme.
Malheureusement il ne fonctionne pas sous Borland C++ 5.5. Le compilo
réclame un 'operator new(unsigned int,void *)'


D'après la documentation de SGI, "construct" est obsolète. L'idée c'était
plutôt de te servir des implémentations fournies par Borland (enfin
j'espère) dans <memory>. L'intérêt est que si jamais ces fonctions existent,
les #include nécessaires seront faits au niveau de <memory>.

Je pense à ça, parce que sous MSVC6 par exemple, il est nécessaire de faire
un #include <new> pour avoir des constructeurs des placement. Inspire toi
des headers de Borland, plutôt que du code de SGI, ils sont adaptés à ton
compilateur.

Patrick Mézard

Avatar
Cyrille \cns\ Szymanski
Je pense à ça, parce que sous MSVC6 par exemple, il est nécessaire de
faire un #include <new> pour avoir des constructeurs des placement.


Oui, c'est exactement ce qui me manquait. Dans mon cas et avec Borland, il
faut juste définir un extern new pour que ça marche.

extern void * operator new(size_t size, void* ptr);

La question que je me pose est de savoir pourquoi ? Est-ce pour permettre à
l'utilisateur de fournir son propre new ?

--
_|_|_| CnS
_|_| for(n=0;b;n++)
_| b&=b-1; /*pp.47 K&R*/

Avatar
Gabriel Dos Reis
"Cyrille "cns" Szymanski" writes:

| La question que je me pose est de savoir pourquoi ?

es-tu sûr de vouloir savoir ?

-- Gaby
Avatar
Gabriel Dos Reis
--=-=- Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: quoted-printable

"Cyrille "cns" Szymanski" writes:

| >| La question que je me pose est de savoir pourquoi ?
| >
| > es-tu sûr de vouloir savoir ?
|
| J'ai dû dire quelquechose qui t'a fâché, non ?

--=-=- Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit


Non. Aucunément. C'était ma façon de dire que si tu ne maîtrises pas
encore les fonctionnalités simples (je ne considère pas la gestion de
mémoire ainsi que la « magie » derrière new/delete comme simple)
alors, tu seras probablement confus par une explication detaillée du
pourquoi et comment du new. D'ailleurs ce que j'ai dit était très
sommaire et j'ai escamoté beaucoup de détails.

-- Gaby


--=-=-=--
Avatar
drkm
Fabien LE LEZ writes:

Il faut utiliser le new de position (placement new). De mémoire (je
l'utilise peu), il faut faire :

new (m_ptr[i]) Obj;


Ne serait-ce pas plutôt :

new ( & m_ptr[i] ) Obj ;

ou, au choix :

new ( m_ptr + i ) Obj ;

Il me semble que le placement new demande une adresse, et non une
référence.

--drkm