C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Comment faire ?

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (French)
View previous topic :: View next topic  
Author Message
Manuel Leclerc
Guest





PostPosted: Wed Nov 22, 2006 11:30 pm    Post subject: Comment faire ? Reply with quote



Je voudrais qu'une fonction renvoie un objet encapsulant
un pointeur allouée par la fonction, de façon à ce que
l'appelant n'ait pas à se soucier de la libération du
pointeur : elle sera faite quand l'objet retourné quitera
la portée dans l'appelant.

exemple

class class amp {
public:
void * m_p;
amp( void * pAllocated ) : m_p( pAllocated ) {}
~amp() { /* libération de p */ }
};

amp f( void ) {
/* ... */
return amp( pAllocated );
}

void g( void ) {

amp NoMemoryLeak = f();
}

Mon problème : si je ne défini pas un
constructeur de copie, la classe devient un peu
dangeureuse. Si je le défini, n'ai je pas un
problème de performance important ?
(réallocation / désallocation "inutiles" au moment
de l'appel de fonction)
Back to top
IR
Guest





PostPosted: Wed Nov 22, 2006 11:30 pm    Post subject: Re: Comment faire ? Reply with quote



Manuel Leclerc wrote:
Quote:
Je voudrais qu'une fonction renvoie un objet encapsulant
un pointeur allouée par la fonction, de façon à ce que
l'appelant n'ait pas à se soucier de la libération du
pointeur : elle sera faite quand l'objet retourné quitera
la portée dans l'appelant.
[snip]


Il y a une raison précise pour ne pas utiliser std::auto_ptr?

--
IR
Back to top
Sylvain Togni
Guest





PostPosted: Thu Nov 23, 2006 12:19 am    Post subject: Re: Comment faire ? Reply with quote



Manuel Leclerc wrote:

Quote:
class class amp {
public:
void * m_p;
amp( void * pAllocated ) : m_p( pAllocated ) {}
~amp() { /* libération de p */ }
};

amp f( void ) {
/* ... */
return amp( pAllocated );
}

void g( void ) {

amp NoMemoryLeak = f();
}

Mon problème : si je ne défini pas un
constructeur de copie, la classe devient un peu
dangeureuse. Si je le défini, n'ai je pas un
problème de performance important ?
(réallocation / désallocation "inutiles" au moment
de l'appel de fonction)

Regarde du côté de std::auto_ptr ou boost::share_ptr, ils
implémentent des mécanismes permettant d'éviter les copies
inutiles.

Pour une utilisation en retour de fonction, std::auto_ptr
convient, il utilise la notion d'"ownership" pour déterminer
quand désallouer l'objet.

--
Sylvain Togni
Back to top
Manuel Leclerc
Guest





PostPosted: Thu Nov 23, 2006 12:58 am    Post subject: Re: Comment faire ? Reply with quote

IR a écrit :

Quote:
Il y a une raison précise pour ne pas utiliser std::auto_ptr?

la ressource que j'encapsule n'a pas été allouée avec new...

Mais du coup je me suis demandé comment auto_ptr prenait
en compte les histoires de copies et de retour de
fonction, et je viens de lire :
http://www.awprofessional.com/content/images/020163371X/autoptrupdate%5Cauto_ptr_update.html

Ca fait peur !
Back to top
Sylvain
Guest





PostPosted: Thu Nov 23, 2006 3:02 am    Post subject: Re: Comment faire ? Reply with quote

Manuel Leclerc wrote on 22/11/2006 18:30:
Quote:
Je voudrais qu'une fonction renvoie un objet encapsulant
un pointeur allouée par la fonction, de façon à ce que
l'appelant n'ait pas à se soucier de la libération du
pointeur : elle sera faite quand l'objet retourné quitera
la portée dans l'appelant.

cette "simplicité" pour l'appelant peut être une raison, il peut s'agir
également de fournir une facade utilisant une instance interne (m_p) qui
est inconnu de amp ou volontairement masquée.

Quote:
Mon problème : si je ne défini pas un
constructeur de copie, la classe devient un peu
dangeureuse. Si je le défini, n'ai je pas un
problème de performance important ?

dans les 2 cas, un reference counting sur la classe interne sera
nécessaire, et cela adresse justement les performances (comme le fait
que cette classe interne peut -elle- ne pas supporter la copie).

soit par exemple (je remplace void* par qlq chose de plus expressif)

class class amp {
public:
// public interface for 'amp' service
class iAmp {
private:
long counter;
public:
iAmp() { counter = 0; }
virtual ~iAmp() {}
// referencement
long addRef() { return ++counter; }
long release() { return --counter; }
// service(s)
virtual void foo() = null;
};

protected:
iAmp* m_p; // opaque provider

public:
explicit amp(iAmp* p){
// initialisation par iAmp existant
if ((m_p = p) != null)
m_p->addRef();
}
amp(const amp& other){
// un 'amp' est créé, le même iAmp est utilisé
if ((m_p = other.m_p) != null)
m_p->addRef();
}
amp& operator= (const amp& other){
// utilise le iAmp de 'other', libère le précédent si <>
iAmp* old = m_p;
if ((m_p = other.m_p) != null)
m_p->addRef();
if (old)
old->release();
return *this;
}
virtual ~amp(){
// liberation conditionnel du provider
if (m_p && m_p->release() == 0)
delete m_p;
}

// access aux services
void foo() { if (m_p) m_p->foo(); }
}

notez que l'on détruit (de manière conditionnel) le iAmp dans le
destructeur de amp; on pourrait choisir de le faire dans iAmp::release,
toutefois si m_p est (vraiment) une classe opaque je préfère cette
version, elle évite de plus les risques de destruction alors que la
référence existe encore dans des 'amp' (suite à un mauvais décompte).

Sylvain.
Back to top
Guest






PostPosted: Thu Nov 23, 2006 3:14 am    Post subject: Re: Comment faire ? Reply with quote

Manuel Leclerc wrote:

Quote:
class class amp {
public:
void * m_p;
amp( void * pAllocated ) : m_p( pAllocated ) {}
~amp() { /* libération de p */ }
};

amp f( void ) {
/* ... */
return amp( pAllocated );
}

void g( void ) {

amp NoMemoryLeak = f();
}

Mon problème : si je ne défini pas un
constructeur de copie, la classe devient un peu
dangeureuse. Si je le défini, n'ai je pas un
problème de performance important ?
(réallocation / désallocation "inutiles" au moment
de l'appel de fonction)


Pour le code que tu présentes précisément, la question ne se pose
pas. En effet, si ton compilateur dispose des 2 optimisations (très
répandues) suivantes :
- RVO
- optimisation de l'initilisation d'un objet par un objet temporaire

ton code ne fera pas appel à un constructeur de copie (la construction
de l'objet retourné "amp" tiendra place directement dans l'espace
mémoire alloué à "NoMemoryLeak").

Ensuite, bien entendu, il faut examiner toutes les utilisations
potentielles d'objets de type "amp" , mais il ne vaut mieux pas se
référer à cette notion de classe "un peu dangereuse", soit elle
marche dans tous les cas, soit elle ne marche pas dans certains cas :
c'est ce qui commande la décision d'utiliser ou non un mécanisme de
réplication d'objets sous-jacents.
Back to top
Sylvain
Guest





PostPosted: Thu Nov 23, 2006 4:57 am    Post subject: Re: Comment faire ? Reply with quote

Manuel Leclerc wrote on 22/11/2006 19:58:
Quote:
IR a écrit :

Il y a une raison précise pour ne pas utiliser std::auto_ptr?

la ressource que j'encapsule n'a pas été allouée avec new...

Mais du coup je me suis demandé comment auto_ptr prenait
en compte les histoires de copies et de retour de
fonction, et je viens de lire :

"l'histoire de la copie" avec std::auto_ptr est simple, il ne copie pas,
il transfère la propriété (enfin à ce que j'ai compris).

dès lors si le constructeur de copie (amp(const amp&)) ou l'operateur de
copie (operator= (const amp&)) sont utilisés pour effectivement avoir
plusieurs instances qui utilisent le même m_p (et non juste répondre à
comment est stocké le résultat d'un fonction type "amp f();") alors
auto_ptr est inutilisable.

Sylvain.
Back to top
Marc G
Guest





PostPosted: Thu Nov 23, 2006 5:05 am    Post subject: Re: Comment faire ? Reply with quote

je comprends pas trop ton problème...mais j'ai 2 remarques :
- si la ressource n'a pas été allouée avec new dans le fonction f, alors tu
vas avoir de gros problèmes, car tu transmets l'adresse d'un objet
détruit...
- si ta classe amp n'a qu'une donnée membre de type pointeur, un
constructeur de copie ne doit pas être beaucoup moins performant qu'une
initialisation directe !

et si auto_ptr ne te convient pas, tu peux bricoler une classe du genre

template <typename U>
class Ptr
{
public :

//=========================================================================================
// Constructeurs
//=========================================================================================
Ptr(U* x) : _ptr(x) {} //
prend possession d'un objet U alloué avec new
Ptr(U const& x) : _ptr(new U(x)) {} // copie
intégrale d'un objet U
// transfert de la propriété de l'objet
Ptr(Ptr& x) : _ptr(x.reset()) {} //
"transfert de propriété"
//=========================================================================================
~Ptr() { if (_ptr) delete _ptr;}
U* operator->() throw() { return _ptr;}
U& operator*() throw() { return *_ptr;} //
Opérateur * préfixé
U const* operator->() const throw() { return _ptr;}
U const& operator*() const throw() { return *_ptr;} //
Opérateur * préfixé
U* reset(void) { U* old=_ptr; _ptr=NULL; return old;}
private :
U *_ptr;
};

en faisant attention au constructeur que tu utilises, selon ce que tu
souhaites !
Bon courage
Marc
Back to top
Guest






PostPosted: Thu Nov 23, 2006 11:45 pm    Post subject: Re: Comment faire ? Reply with quote

On 23 nov, 15:11, "Manuel Leclerc" <manuel.lecl...@alussinan.org>
wrote:
Quote:
dieu.tout.puiss...@gmail.com a écrit :

amp NoMemoryLeak = f();

Pour le code que tu présentes précisément, la question ne se
pose pas. En effet, si ton compilateur dispose des 2 optimisations
(très répandues) suivantes :
- RVO
- optimisation de l'initilisation d'un objet par un objet temporaire

ton code ne fera pas appel à un constructeur de copie (la construction
de l'objet retourné "amp" tiendra place directement dans l'espace
mémoire alloué à "NoMemoryLeak").Ok. Mon VS2005 n'appelle pas le constructeur de copie en
mode release (il le fait en mode debug).

Il me reste un souci. Je veux pouvoir coder :

bool TestAmp( amp & );
amp NoMemoryLeak;
while ( true ) {
NoMemoryLeak = f();
if ( TestAmp( NoMemoryLeak ) ) break;

}// NoMemoryLeak utilisable ici

Donc, il va me falloir définir l'opérateur d'affectation.

Ai-je un moyen de savoir que je peux capturer
le pointeur présent dans l'objet rendue par la
fonction f de manière à éviter une séquence
copy/free ?

Autrement dit, puis je définir deux opérateurs
d'affectation différents pour les deux cas ci-dessous

A)
amp NoMemoryLeak;
/* */
NoMemoryLeak = f();

B)
amp NoMemoryLeak;
amp NoMemoryLeak2;
/* */
NoMemoryLeak = NoMemoryLeak2;

Si je ne peux pas, je crois qu'il va falloir que j'utilise
l'histoire du comptage de référence proposée par
Sylvain, mais j'aurais préféré éviter Smile


Désolé Smile mais d'après ces cas d'utilisation, le comptage de
référence est le bon choix. Puisque tu as l'air de vouloir éviter au
maximum les copies non nécessaires, il te faut un mécanisme de
Copy-on-Write. Le comptage de référence est le mécanisme le plus
simple implémentant un CoW.
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (French) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.