OVH Cloud OVH Cloud

Recherche dans STL

8 réponses
Avatar
Francis
Bonjour,

J'ai une classe de base "Interface" ainsi qu'une classe dérivée
"InterfaceRS232" qui sont contenues dans un
vecteur "Interface *" sous forme de pointeur.

Je peux ajouter des éléments à mon vecteur et relire ceux-ci.

Le code ci-dessous me pose un souci lorsque je veux rechercher un pointeur
vers un interface contenu dans le vecteur !
La classe interface étant abstraite, elle ne peut être utilisée pour créer
une référence utilisée pour la recherche !

Quel code utiliser pour effectuer cette recherche (VC6 + VS 2003)
Merci de votre aide !

// test.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

class Interface
{
public:
Interface(void) { }
virtual ~Interface(void) { }
int operator==(const Interface& a);
virtual void InitCom(void)=0;
const std::string& getName(void) const { return name; }
protected:
std::string name;
};

class InterfaceRS232 : public Interface
{
public:
InterfaceRS232(std::string comName) { name = comName; }
virtual ~InterfaceRS232() { }
virtual void InitCom() { std::cout << " Init " << getName() <<
std::endl; }
};

typedef std::vector < Interface *> VectorInterface;
std::vector < Interface *>::iterator VectorInterfaceIterator;
VectorInterface createdInterfaces;
VectorInterface tempVectorInterface;

template <typename T>
class DeferencedEqual
{
public:
DeferencedEqual(const T* p) : p_(p) {}
bool operator() (const T* p2) const {return *p_ == *p2; }
private:
const T* p_;
};

void dump(Interface *tempVectorInterface)
{
std::cout << tempVectorInterface->getName() << " ";
}

int main(int argc, char* argv[])
{
InterfaceRS232 *pInterfaceCOM1 = new InterfaceRS232("COM1");
pInterfaceCOM1->InitCom();
createdInterfaces.push_back(pInterfaceCOM1);
InterfaceRS232 *pInterfaceCOM2 = new InterfaceRS232("COM2");
pInterfaceCOM2->InitCom();
createdInterfaces.push_back(pInterfaceCOM2);

std::cout << "Nombre d'interfaces:" << createdInterfaces.size() << '\n';

// Imprimer le contenu du vecteur
std::for_each(createdInterfaces.begin(), createdInterfaces.end(), dump);
std::cout << '\n';

// Rechercher un élément - Code ci-dessous, ERREUR à la compilation
/*
Interface *pInterfaceRecherche = new Interface("COM1");
VectorInterfaceIterator = find(createdInterfaces.begin(),
createdInterfaces.end(),
DeferencedEqual<Interface>(&pInterfaceRecherche));
if (VectorInterfaceIterator == createdInterfaces.end())
{
std::cout << "COM1 non trouvé dans la liste" << '\n';
}
else
{
std::cout << (*VectorInterfaceIterator)->getName() << " trouvé dans la
liste" << '\n';
}
delete pInterfaceRecherche;
*/

delete pInterfaceCOM1;
delete pInterfaceCOM2;
delete pInterfaceCAN;

// clear all elements from the array
std::vector<Interface*>::iterator itPos = createdInterfaces.begin();
for (; itPos < createdInterfaces.end(); itPos++)
;// delete *itPos; // free the element from memory !!! ERROR
//finally, clear all elements from the array
createdInterfaces.clear();

return 0;
}



--
Francis GOBLET
f.goblet@scarlet.be

8 réponses

Avatar
Fabien LE LEZ
On Fri, 6 Oct 2006 22:49:30 +0200, "Francis" :

int operator==(const Interface& a);


Cette fonction devrait peut-être être virtuelle.
En tout cas, ce qui est sûr, c'est qu'elle doit renvoyer un bool, pas
un int.


Interface *pInterfaceRecherche = new Interface("COM1");


Il n'y a aucune raison d'utiliser l'allocation dynamique ici.

Interface interface_recherchee ("COM1");

VectorInterfaceIterator = find(createdInterfaces.begin(),


find_if (createdInterfaces.begin(), createdInterfaces.end(),
DeferencedEqual<Interface>(&interface_recherchee));


createdInterfaces.end(),
DeferencedEqual<Interface>(&pInterfaceRecherche));


Note que dans ton code, "pInterfaceRecherche" était déjà un pointeur,
il ne faut donc pas rajouter un "&".

Avatar
Falk Tannhäuser
Fabien LE LEZ schrieb:
On Fri, 6 Oct 2006 22:49:30 +0200, "Francis" :

int operator==(const Interface& a);


Cette fonction devrait peut-être être virtuelle.
En tout cas, ce qui est sûr, c'est qu'elle doit renvoyer un bool, pas
un int.


En plus, elle devrait être qualifiée "const" :
bool operator==(Interface const& a) const;
Mais il se trouve qu'on n'aura pas besoin de cet opérateur par la suite...

Interface *pInterfaceRecherche = new Interface("COM1");


Il n'y a aucune raison d'utiliser l'allocation dynamique ici.


Surtout, il n'y a aucune raison d'instancier une Interface juste pour
faire une recherche par le nom.

#include <boost/bind.hpp>
...

VectorInterfaceIterator = std::find_if(
createdInterfaces.begin(),
createdInterfaces.end(),
boost::bind(&Interface::getName, _1) == "COM1");


Ou, si on ne [pv]eut pas utiliser Boost:

struct Cmp_getName
{
std::string name;
Cmp_getName(std::string n) : name(n) {}

bool operator()(Interface const* itf) const
{ return itf->getName() == name; }
};
...

VectorInterfaceIterator = std::find_if(createdInterfaces.begin(),
createdInterfaces.end(),
Cmp_getName("COM1"));

Falk


Avatar
Francis
Merci pour votre aide, cela fait exactement ce que je veux.

Pour la suppression des éléments, le code suivant est-il correct
// clear all elements from the array
std::vector<Interface*>::iterator itPos = createdInterfaces.begin();
for (; itPos < createdInterfaces.end(); itPos++)
{
createdInterfaces.erase(itPos);
//delete createdInterfaces[itPos]; // free the element from memory
}
//finally, clear all elements from the array
createdInterfaces.clear();

Francis

"Falk Tannhäuser" a écrit dans le message de
news: 4526e92d$0$21896$
Fabien LE LEZ schrieb:
On Fri, 6 Oct 2006 22:49:30 +0200, "Francis" :

int operator==(const Interface& a);


Cette fonction devrait peut-être être virtuelle.
En tout cas, ce qui est sûr, c'est qu'elle doit renvoyer un bool, pas
un int.


En plus, elle devrait être qualifiée "const" :
bool operator==(Interface const& a) const;
Mais il se trouve qu'on n'aura pas besoin de cet opérateur par la suite...

Interface *pInterfaceRecherche = new Interface("COM1");


Il n'y a aucune raison d'utiliser l'allocation dynamique ici.


Surtout, il n'y a aucune raison d'instancier une Interface juste pour
faire une recherche par le nom.

#include <boost/bind.hpp>
...

VectorInterfaceIterator = std::find_if(
createdInterfaces.begin(),
createdInterfaces.end(),
boost::bind(&Interface::getName, _1) == "COM1");


Ou, si on ne [pv]eut pas utiliser Boost:

struct Cmp_getName
{
std::string name;
Cmp_getName(std::string n) : name(n) {}

bool operator()(Interface const* itf) const
{ return itf->getName() == name; }
};
...

VectorInterfaceIterator = std::find_if(createdInterfaces.begin(),
createdInterfaces.end(),
Cmp_getName("COM1"));

Falk




Avatar
Cyrille
Merci pour votre aide, cela fait exactement ce que je veux.

Pour la suppression des éléments, le code suivant est-il correct
// clear all elements from the array
std::vector<Interface*>::iterator itPos = createdInterfaces.begin();
for (; itPos < createdInterfaces.end(); itPos++)
{
createdInterfaces.erase(itPos);
//delete createdInterfaces[itPos]; // free the element from memory
}
//finally, clear all elements from the array
createdInterfaces.clear();


gni?

createdInterfaces.erase(itPos) rend invalide les itérateurs sur le
vector, on ne peut pas utiliser ça dans une boucle sur les éléments.

Si tu veux enlever tous les éléments du vector, utilise simplement
createInterfaces.clear().
Si tu veux faire un delete sur tous les éléments du vector, il faut
précéder l'appel à clear() de:
std::vector<Interface*>::iterator itPos = createdInterfaces.begin();
for (; itPos < createdInterfaces.end(); itPos++)
{
delete *itPos;
}

--
Les lois sont toujours utiles à ceux qui possèdent et nuisibles à ceux
qui n'ont rien. ~ Rousseau, du Contrat Social.

Avatar
Francis
C'est bien ce que j'avais vu dans un exemple, mais ce code génère une
exception au delete
Violation d'accès --> lecture emplacement 0xfeeefeee

D'ou effacement avec createdInterfaces.erease(itPos) qui ne génère pas
d'erreur comme dans mon exemple (vu dans un autre exemple)

Francis

"Cyrille" a écrit dans le message de news:
4527828b$0$21146$
Merci pour votre aide, cela fait exactement ce que je veux.

Pour la suppression des éléments, le code suivant est-il correct
// clear all elements from the array
std::vector<Interface*>::iterator itPos = createdInterfaces.begin();
for (; itPos < createdInterfaces.end(); itPos++)
{
createdInterfaces.erase(itPos);
//delete createdInterfaces[itPos]; // free the element from memory
}
//finally, clear all elements from the array
createdInterfaces.clear();


gni?

createdInterfaces.erase(itPos) rend invalide les itérateurs sur le vector,
on ne peut pas utiliser ça dans une boucle sur les éléments.

Si tu veux enlever tous les éléments du vector, utilise simplement
createInterfaces.clear().
Si tu veux faire un delete sur tous les éléments du vector, il faut
précéder l'appel à clear() de:
std::vector<Interface*>::iterator itPos = createdInterfaces.begin();
for (; itPos < createdInterfaces.end(); itPos++)
{
delete *itPos;
}

--
Les lois sont toujours utiles à ceux qui possèdent et nuisibles à ceux qui
n'ont rien. ~ Rousseau, du Contrat Social.



Avatar
Cyrille
C'est bien ce que j'avais vu dans un exemple, mais ce code génère une
exception au delete
Violation d'accès --> lecture emplacement 0xfeeefeee

D'ou effacement avec createdInterfaces.erease(itPos) qui ne génère pas
d'erreur comme dans mon exemple (vu dans un autre exemple)


Mais ça ne fait pas du tout la même chose.

--
Les lois sont toujours utiles à ceux qui possèdent et nuisibles à ceux
qui n'ont rien. ~ Rousseau, du Contrat Social.

Avatar
James Kanze
Fabien LE LEZ wrote:
On Fri, 6 Oct 2006 22:49:30 +0200, "Francis" :

int operator==(const Interface& a);


Cette fonction devrait peut-être être virtuelle.


Ça m'étonnerait. Je doute plutôt qu'elle doit exister. La
comparaison, comme l'affectation (et, d'ailleurs, la plupart des
opérateurs) ne marche pas particulièrement bien avec des objets
polymorphique.

Interface *pInterfaceRecherche = new Interface("COM1");


Il n'y a aucune raison d'utiliser l'allocation dynamique ici.


Il n'y a aucune raison d'utiliser un objet de type Interface (ou
d'une classe qui en dérive) ici. Il veut simplement comparer des
chaînes de caractères, non des objets entiers.

Interface interface_recherchee ("COM1");

VectorInterfaceIterator = find(createdInterfaces.begin(),


find_if (createdInterfaces.begin(), createdInterfaces.end(),
DeferencedEqual<Interface>(&interface_recherchee));


Beaucoup mieux :

class IsName
{
public:
IsName( std::string const& name )
: target( name )
{
}

bool operator()( Interface const* obj ) const
{
return obj->getName() == target ;
}

private:
std::string target ;
} ;

et ensuite, un find_if avec « IsName( "COM1" ) » comme troisième
paramètre.

--
James


Avatar
James Kanze
Francis wrote:

Pour la suppression des éléments, le code suivant est-il correct
// clear all elements from the array
std::vector<Interface*>::iterator itPos = createdInterfaces.begin();
for (; itPos < createdInterfaces.end(); itPos++)


Attention : l'opérateur < ne fonctionne pas avec tout type
d'itérateur. En général, on préfère :

itPos != createdInterfaces.end() ;

{
createdInterfaces.erase(itPos);
//delete createdInterfaces[itPos]; // free the element from memory
}
//finally, clear all elements from the array
createdInterfaces.clear();


Non. L'appel à erase invalide l'intérateur.

L'idiome consacré pour effacer des éléments d'une collection,
c'est :

Collection::iterator iter = collection.begin() ;
while ( iter != collection.end() ) {
if ( condition( *iter ) ) {
iter = collection.erase( iter ) ;
} else {
++ iter ;
}
}

Si la condition est toujours vrai, évidemment, on peut
simplifier:-).

Ici, en revanche, il se peut que tu veux effectuer des
opérations sur l'élément que tu effaces, disons, peut-être un
delete. Alors :

Collection::iterator iter = collection.begin() ;
while ( iter != collection.end() ) {
if ( condition( *iter ) ) {
delete *iter ;
iter = collection.erase( iter ) ;
} else {
++ iter ;
}
}

(Juste un detail : formellement, le code ci-dessus contient un
comportement indéfini. Suite au delete, le pointeur est
invalid ; on ne peut même pas le regarder. Et la norme exige que
tous les éléments dans une collection soient valides, toujours.
Dans la pratique, il ne ferait jamais de problème ; il y a bien
peu d'implémentations où lire le pointeur après un delete pose
un problème, et de toute façon, l'implémentation de la
collection ne va pas le lire sans raison. Mais si tu veux être
pointieux, dans l'if, il faudrait écrire :

Interface* tmp = *iter ;
iter = collection.erase( iter ) ;
delete tmp ;

Mais dans la pratique, le faire, c'est juste pour faire plaisir
aux emmerdeurs comme moi. )

Enfin, si on va supprimer toute la collection par la suite,
c'est encore plus facile, parce qu'on n'a pas besoin de l'erase
dans la boucle :

for ( Collection::iterator iter = collection.begin() ;
iter != collection.end() ;
++ iter ) {
Interface* tmp = NULL ;
std::swap( tmp, *iter ) ;
delete tmp ;
}
collection.clear() ;

(Mais évidemment, un simple « delete *iter » suffit dans la
pratique, même si la norme dit autrement. En revanche,
l'utilisaton de std::swap est très « in », si tu veux être à la
mode.)

--
James