J'ai une question sur la façon dont les pointeurs vers un objet personnalisé sont gérés lorsqu'ils sont utilisés comme clés dans une carte. Plus précisément si je définis
std::map< CustomClass*, int > foo;
L'implémentation C++ par défaut fonctionnerait-elle pour gérer ces pointeurs? Ou dois-je définir une fonction de comparaison personnalisée pour la gérer? En général, est-ce une bonne pratique d'utiliser des pointeurs vers des objets comme clés?
L'implémentation par défaut comparera les adresses stockées par les pointeurs, donc différents objets seront considérés comme des clés différentes. Cependant, l'état logique de l'objet ne sera pas pris en compte. Par exemple, si vous utilisez std::string *
Comme clé, deux objets std::string
Différents avec le même texte de "Hello"
Seraient considérés comme une clé différente! (Lorsque stocké dans la carte par leurs adresses)
Vous pouvez utiliser des pointeurs comme clés tant que vous comprenez la différence importante ci-dessus.
Les pointeurs seront traités mais comparés comme des pointeurs (ordre de la mémoire). Vous devez passer un foncteur less
personnalisé si vous souhaitez comparer les objets:
template<class T> struct ptr_less {
bool operator()(T* lhs, T* rhs) {
return *lhs < *rhs; }};
map<Object*,int,ptr_less<Object>> mymap;
La norme C++ fournit une spécialisation de std::less
pour les pointeurs, donc oui, vous pouvez les utiliser en toute sécurité comme clés de carte, etc.
En dehors de la légalité de ce problème et de tout malentendu sémantique éventuel, déjà abordé, je ne vois aucune raison d'utiliser std::map
ici plutôt que std::unordered_map
. Il existe des interceptions précoces de cela dans Boost et Visual C++ si vous êtes sur un compilateur pré-C++ 11.
Comme vous semblez utiliser un pointeur pour représenter un objet unique, quelque chose comme boost :: flyweight pourrait être applicable.
Les pointeurs peuvent être utilisés comme clés mais surtout avec un std :: map (ou std :: set) je ne le conseillerais pas. Le comportement du programme n'est pas déterministe, c'est-à-dire que lorsque l'on parcourt la carte, l'ordre dans lequel les éléments de la carte sont itérés n'est pas garanti d'être le même . Cela dépend vraiment de l'adresse mémoire de l'objet (clé). Jetez un œil à cet exemple, comme vous pouvez le voir, quel que soit l'ordre d'insertion dans la carte, les éléments sont itérés de manière déterministe lorsque la clé est une chaîne plutôt qu'un pointeur.
#include <iostream>
#include <map>
using namespace std;
class SomeClass {
public:
SomeClass(const std::string& name): m_name(name) {}
std::string GetName()const {return m_name; }
bool operator <(const SomeClass& rhs) const { return m_name < rhs.m_name; }
private:
std::string m_name;
};
auto print_seq = [](const auto& seq) { for (const auto& itr: seq) {std::cout << itr.second << " , ";} std::cout << std::endl;};
int main() {
// your code goes here
std::map<SomeClass*, std::string> pointer_keyed_map;
SomeClass s3("object3");
SomeClass s1("object1");
SomeClass s2("object2");
pointer_keyed_map.insert(std::make_pair(&s1, s1.GetName()));
pointer_keyed_map.insert(std::make_pair(&s2, s2.GetName()));
pointer_keyed_map.insert(std::make_pair(&s3, s3.GetName()));
std::cout << "Pointer based keys: object order" << std::endl;
print_seq(pointer_keyed_map);
std::map<SomeClass, std::string> int_keyed_map;
int_keyed_map.insert(std::make_pair(s3, s3.GetName()));
int_keyed_map.insert(std::make_pair(s1, s1.GetName()));
int_keyed_map.insert(std::make_pair(s2, s2.GetName()));
std::cout << "String based keys: object order" << std::endl;
print_seq(int_keyed_map);
return 0;
}