4
votes

Insérer un pointeur d'objet dans une carte de cartes via emplace () ne fonctionne pas

J'essaye d'insérer un objet pointeur vers une carte via emplace () mais cela ne fonctionne pas.

J'ai créé une représentation simple de le problème ci-dessous. J'essaie d'insérer dans le type d'objet pointeur newFooList Foo*.

Je n'arrive pas à trouver un moyen de créer un type pour FooMap * dans std :: map m_fooMapList . Doit-il être fait avec le new sur le deuxième champ de la carte?

$  g++ -std=c++11 -Wall map_of_map.cpp 
map_of_map.cpp: In member function ‘void FooMapList::insertFoo(Foo*)’:
map_of_map.cpp:51:18: error: ‘class std::map<int, FooMap*>’ has no member named ‘second’
     m_fooMapList.second = new FooMap;

Sur g ++ (GCC) 4.8.5 20150623 (Red Hat 4.8 .5-28)

#include <iostream>
#include <utility>
#include <stdint.h>
#include <cstdlib>
#include <map>

class Foo
{
    private:
        int m_foobar;
    public:
        Foo(int value)
        {
            m_foobar = value;
        }
        void setfoobar(int value);
        int getfoobar();
};

class FooMap
{
    private:
        std::map<int, Foo*> m_newFoo;

    public:
        FooMap() = default;
};

class FooMapList
{
    private:
        std::map<int, FooMap*> m_fooMapList;
    public:
        FooMapList() = default;
        void insertFoo(Foo* newFooObj);
};

int Foo::getfoobar(void)
{
    return(m_foobar);
}

void FooMapList::insertFoo(Foo* newFooObj)
{
    if(m_fooMapList.empty())
    {
        std::cout << "m_fooMapList is empty" << std::endl ;
    }

    //m_fooMapList.emplace( newFooObj->getfoobar(), newFooObj  );
    // Need to find a way to insert newFooObj  to m_fooMapList
    m_fooMapList.second = new FooMap;
}

int main() {
    FooMapList newFooList;

    for (auto i=1; i<=5; i++)
    {
        Foo *newFoo = new Foo(i);
        newFoo->getfoobar();
        newFooList.insertFoo(newFoo);
    }

    return 0;
}


8 commentaires

Vous avez une liste L de cartes M d'éléments E. Et votre code indique d'insérer E dans L sans logique pour trouver dans quelle carte insérer l'élément.


quelle est l'erreur de la ligne commentée? votre ligne m_fooMapList.second = ... n'a aucun sens, une carte n'a pas de membre second


@Rerito: Donc, pour clarifier, la carte doit être créée avec newFooObj-> getfoobar () comme clé, c'est pourquoi j'ai essayé l'option emplace qui n'a pas fonctionné et a généré un tas d'erreurs STL pointant vers Dans le fichier inclus depuis /usr/include/c++/4.8.2/map:60:0


getfoobar () vous donne la clé de l'élément, mais comment obtiendriez-vous la bonne carte à utiliser dans votre liste?


vous avez une carte de cartes de Foo s, voulez-vous utiliser getfoobar () pour la clé des deux cartes?


@kmdreko: Oui c'est vrai


Pouvez-vous expliquer quel problème vous essayez de résoudre en anglais simple . Votre approche d'une carte de pointeurs vers des cartes de poiinters vers des objets ne semble pas être une solution appropriée. Que devrait-il se passer avec vos cartes, si l'index (m_foobar) est modifié pour un objet Foo? Comment allez-vous éviter les fuites de mémoire ou la double suppression lors du nettoyage ou de la suppression de membres? - Vous avez déclaré que vous ne voulez PAS avoir tous les objets (que vous créez avec l'opérateur «new») sur le tas? - Tout ce que vous créez avec «nouveau» est sur le tas ...


@CAF: J'ai essayé d'expliquer la question du mieux que je pouvais, je ne sais pas ce que vous entendez par un anglais simple. En ce qui concerne toutes vos conditions, j'essaierai de m'améliorer. Comme je l’ai indiqué dans l’un des commentaires, j’apprends simplement la langue et j’ai fait de mon mieux pour le moment. Si vous pouvez améliorer ma réponse, n'hésitez pas à modifier ma réponse, je l'apprécierais vraiment. Merci!


5 Réponses :


1
votes

Sauf si vous avez une très bonne raison de le faire, évitez de brouiller les choses à la Java comme celle-ci et essayez de tirer parti de la STL quand vous le pouvez. À cette fin, vous pouvez utiliser les alias de type

auto FooMap::emplace(int key, Foo* value)
{
    return m_newFoo.emplace(key, value);
}

void FooMapList::insertFoo(Foo* newFooObj)
{
    // If the map for `getfoobar` does not exist yet, operator[] will create it
    auto& mapPtr = m_fooMapList[newFooObj->getfoobar()];
    if (nullptr == mapPtr)
        mapPtr = new FooMap();

    mapPtr->emplace(
        newFooObj->getfoobar(),
        newFooObj
    );
}

Maintenant, vous avez un élément Foo que vous venez de créer et souhaitez l'insérer dans votre liste de cartes, pour ce faire, vous besoin d'un moyen de sélectionner dans quelle carte de la liste vous souhaitez l'insérer. Je suppose que vous insérerez dans la première carte de la liste:

using FooMap = std::map<int, Foo*>; // Maybe use a smart pointer instead here?
using FooMapList = std::map<int, FooMap>; // Maybe List is not an appropriate name for a map

Notez que je n'ai pas géré le nettoyage de la mémoire. Je vous suggère d'essayer d'utiliser des pointeurs intelligents le cas échéant ( std :: unique_ptr et std::shared_ptr)


4 commentaires

Cela semblait correct, mais obtenir une erreur comme map_of_map.cpp: 51: 42: error: request for member 'emplace' in '((FooMapList *) this) -> FooMapList :: m_fooMapList.std :: map <_Key‌ , _Tp, _Compare, _Alloc> :: operator [] , std :: allocator >> ((* & newFooObj -> Foo :: getfoobar ())) ', qui est de type pointeur' std :: map :: mapped_type {aka FooMap *} '(peut-être que vous vouliez utiliser' -> '?) M_fooMapList [newFooObj-> getfoobar ()]. ​​emplace (nouveauFooObj-> getfoobar (), newFooObj);


autant j'adorerais rejoindre une diatribe java, je ne comprends pas vraiment en quoi cela est pertinent ici ou ce que vous entendez réellement par "obscurcir les choses à la Java comme ça", comme quoi?


@ user463035818 En facadant les cartes comme ceci, vous ne pouvez pas utiliser l'interface STL car elle est complètement encapsulée. Cela vous oblige à exposer manuellement les bits qui vous intéressent. C'est une énorme perte de généricité et d'utilisation potentielle avec les algorithmes STL. Par conséquent, je pense toujours que "l'encapsulation" pour des raisons "d'encapsulation" n'est pas souhaitable.


@Inian Désolé, j'ai oublié que la carte était enveloppée dans un objet, exposez simplement une fonction emplace dans votre objet FooMap comme je l'ai fait.



6
votes

m_fooMapList est défini comme

    std::map<int, FooMap> m_fooMapList; // no pointers

    m_fooMapList.emplace(newFooObj->getfoobar(), {}); // construct objects in-place

Donc pour l'insérer, vous avez besoin d'un int et d'un pointeur vers FooMap :

    m_fooMapList.emplace(newFooObj->getfoobar(), new FooMap);

Cela dit, vous devriez utiliser la sémantique des valeurs C ++ et moins vous fier aux pointeurs bruts:

    std::map<int, FooMap*> m_fooMapList;


10 commentaires

Merci j'aime cette option pour créer avec des pointeurs. Mais après avoir créé le nouvel objet FooMap . Pouvez-vous expliquer comment ajouter un nouveau Foo * à cela?


Est-il également faux d'utiliser make_pair avec insert () comme m_fooMapList.emplace (std :: make_pair (newFooObj-> getfoobar (), new FooMap)) . Il semble bien compiler


Comment faire des insertions dans la FooMap maintenant? faites le moi savoir à ce sujet.


insert copie la paire dans la carte, emplace la crée sur place. Le résultat est le même. Concernant votre autre question - c'est déjà une autre question, veuillez la poser séparément. Veuillez également expliquer ce que vous souhaitez réaliser. Ce que vous voulez réellement n'est pas clair.


@Inian Vous ne devriez pas utiliser new . Il n'y a aucune raison ici pour les pointeurs dans ce code. Les supprimer rend tout tellement plus facile. Je serais tenté de remplacer FooMap par en utilisant FooMap = std :: map ;


@rustyx: Désolé si j'étais vague dans ma question. Je veux juste savoir. Comment faire une insertion de Foo * dans la FooMap nouvellement créée. J'ai essayé m_fooMapList.emplace (newFooObj-> getfoobar (), newFooObj); qui ne fonctionnait pas si vous y êtes référencé


Vous avez besoin de deux clés, une pour la carte extérieure, une pour la carte intérieure m_fooMapList [key1] [key2] = Foo (i);


@Caleth: Pouvez-vous ajouter une réponse avec cette logique. J'apprécierai vraiment cela. J'apprends toujours la langue et je ne sais pas encore comment le faire


@Inian, veuillez expliquer votre structure de données. Pourquoi est-ce une carte de cartes? Pourquoi pas une carte de listes?


@rustyx: Parce que sur l'exemple réel du mien, je dois créer des milliers d'objets de temps en temps où l'index varie pour la carte externe et la carte interne. Au niveau de base se trouve le Foo * qui doit être ajouté au niveau supérieur à partir de m_fooMapList . Pouvez-vous me montrer comment je fais ça?



3
votes

Je ne sais pas si vous avez besoin d'une structure de carte où les valeurs sont des pointeurs vers une autre carte. La classe FooMapList pourrait être simple

1
m_fooMapList is empty
inserting to FooMap...
inserting to fooMapList...
2
inserting to FooMap...
inserting to fooMapList...
3
inserting to FooMap...
inserting to fooMapList...
4
inserting to FooMap...
inserting to fooMapList...
5
inserting to FooMap...
inserting to fooMapList...

D'un autre côté, le jeu entier avec le pointeur de ligne ne vous apportera rien d'autre qu'une douleur au cou. P >

En cas d'utilisation de std :: map m_fooMapList; et std :: map sont nécessaires, j'opterais pour les smartpointers.

Voici un exemple de code avec le remplacement des pointeurs de ligne par std :: unique_ptr et montre comment insérer une carte de Foo s à carte sur place. Voir en direct ici

#include <iostream>
#include <utility>
#include <map>
#include <memory>

class Foo
{
private:
    int m_foobar;
public:
    Foo(int value): m_foobar(value) {}
    void setfoobar(int value) noexcept { m_foobar = value; }
    int getfoobar() const noexcept { return m_foobar; }
};

class FooMap
{
private:
    std::map<int, std::unique_ptr<Foo>> m_newFoo;
    //            ^^^^^^^^^^^^^^^^^^^^
public:
    FooMap() = default;
#if 0 // optional
    // copy disabled
    FooMap(const FooMap&) = delete;
    FooMap& operator=(const FooMap&) = delete;

    // move enabled
    FooMap(FooMap&&) = default;
    FooMap& operator=(FooMap&&) = default;
#endif
    // provide a helper function to insert new Foo to the map of Foo s
    void insertFoo(std::unique_ptr<Foo> newFooObj)
    //             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    {
        std::cout << "inserting to FooMap..." << std::endl;
        m_newFoo.emplace(newFooObj->getfoobar(), std::move(newFooObj)); // construct in place
    }
};

class FooMapList
{
private:
    std::map<int, std::unique_ptr<FooMap>> m_fooMapList;
    //            ^^^^^^^^^^^^^^^^^^^^^^^
public:
    FooMapList() = default;

    void insertFooMap(std::unique_ptr<Foo> newFooObj)
    //               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    {
        if (m_fooMapList.empty())
        {
            std::cout << "m_fooMapList is empty" << std::endl;
        }
        // create FooMap and insert Foo to it.
        FooMap fooMap;
        const auto key = newFooObj->getfoobar();
        fooMap.insertFoo(std::move(newFooObj));

        // finally insert the FooMap to m_fooMapList
        std::cout << "inserting to fooMapList..." << std::endl;
        m_fooMapList.emplace(key, std::make_unique<FooMap>(std::move(fooMap))); // construct in place
    }
};

int main() 
{
    FooMapList newFooList;

    for (auto i = 1; i <= 5; i++)
    {
        auto newFoo = std::make_unique<Foo>(i);
        std::cout << newFoo->getfoobar() << std::endl;
        newFooList.insertFooMap(std::move(newFoo));
    }

    return 0;
}

Sortie :

std::map<int, FooMap> m_fooMapList;


11 commentaires

C'est bien! mais supportant la douleur dans le cou, j'ai encore besoin d'utiliser des pointeurs, car sur le vrai exemple du mien, j'ai besoin de créer des milliers d'objets de temps en temps, ce qui doit être fait dynamiquement sur lequel je ne veux pas tenir le coup le tas


Alors n'oubliez pas de vous soucier également de la règle de trois / cinq / zéro . Bon codage;)


Merci beaucoup! Pourriez-vous me faire part de l'autre réponse de rustyx qui montrait comment utiliser le new pour créer le FooMap . Je manque juste cette étape pour ajouter par la suite un Foo * à cela. Ce sera formidable si vous pouvez suggérer à ce sujet


@Inian Les objets Foo dans une carte sont alloués dynamiquement par la carte


^^ en d'autres termes (en plus du commentaire de @ Caleth), peu importe ce que vous avez dans std :: map est sur le tas. Donc std :: map m_fooMapList; suffirait.


@Inian De la même manière que pour m_fooMapList . => m_newFoo.emplace (nouveauFooObj-> getfoobar (), nouveau Foo (valeur));


@JeJo: Merci d'avoir été patient avec moi. Mais mon problème depuis le début a été de savoir comment accéder à cette nouvellement FooMap créée? comment accéder à m_newFoo dans ma logique. Merci! Pourriez-vous mettre à jour votre réponse avec la même chose? Merci beaucoup


@Inian comment puis-je accéder à cette FooMap nouvellement créée? : Je suppose que vous voulez y ajouter plus de Foo ; pas juste un Foo , est-ce exact? Si le moyen le plus simple est de créer un FooMap dans main-> ajoutez Foo s puis insérez-le dans la FooMapList : la fonction serait < code> void insertFooMap (std :: unique_ptr newFooMapObj) au lieu de seulement FooObj .


@Inian Pensez également aux fonctions getter où vous pouvez lui donner une clé et qui renvoie la valeur correspondante (FooMap) à partir de la m_fooMapList (s'il existe).


@JeJo: Je n'arrive toujours pas à faire ce travail. J'ai essayé toutes les combinaisons avec la création de références de pointeur. Dans ma vraie logique, je ne peux pas créer FooMap à partir de main (). Ce dont j'ai besoin, c'est exactement votre logique, mais avec des pointeurs. Veuillez partager votre approche pour le même


@Inian Ensuite, supprimez simplement partie de pointeur intelligent que je ne recommanderai pas. Faites attention aux pointeurs de ligne BTW.



2
votes

Vous pouvez jeter vos classes de carte à ne rien faire, cesser d'utiliser des pointeurs, et simplement

#include <iostream>
#include <utility>
#include <stdint.h>
#include <cstdlib>
#include <map>

class Foo
{
private:
    int m_foobar;
public:
    Foo(int value) : m_foobar(value) { }
    void setfoobar(int value) { m_foobar = value; }
    int getfoobar() const { return m_foobar; }

    // or more simply
    // int foobar;
};

using FooMap = std::map<int, Foo>;

using FooMapMap = std::map<int, FooMap>;

int main() {
    FooMapMap foos;

    for (auto i=1; i<=5; i++)
    {
        foos[i][i] = Foo(i);
    }

    return 0;
}

Notez que la carte interne est totalement inutile à ce stade , car ils n'ont qu'une seule entrée


0 commentaires

0
votes

J'ai pris en compte les points valides de chacune des réponses pour supprimer les pointeurs et supprimer les représentations cartographiques à double niveau inutiles. Mais l'abstraction du monde réel est un problème très complexe qui implique des milliers d'objets à la volée qui doivent être créés et détruits dynamiquement. Utiliser des pointeurs semblait une approche valable, mais l ' l'approche de JeJo me paraissait bien meilleure.

J'ai essayé de réutiliser sa tentative mais avec des pointeurs d'objet et ci-dessous semble fonctionner. Avec les fonctions d'insertion ci-dessous

Dans la classe FooMap , la fonction serait

void FooMapList::insertFooList(Foo* newFooObj)
{
    std::map <int, FooMap*>::iterator iter;
    FooMap *localFooMap = NULL;
    iter = m_fooMapList.find( newFooObj->getfoobar() );

    if( iter == m_fooMapList.end() )
    {
        localFooMap = new FooMap;
        localFooMap->insertFoo(newFooObj);
        m_fooMapList.emplace(newFooObj->getfoobar(), localFooMap );
    }
    else
    {    
        localFooMap = iter->second;
        localFooMap->insertFoo(newFooObj);
        m_fooMapList.emplace(newFooObj->getfoobar(), localFooMap );
    }
}

const std::map<int, FooMap*> FooMapList::getList()
{
    return m_fooMapList;
}

et dans FooMapList code> ce serait

void FooMap::insertFoo(Foo* newFooObj)
{
    m_newFoo.emplace(newFooObj->getfoobar(), newFooObj);
}

const std::map<int, Foo*> FooMap::getList()
{
    return m_newFoo;
}

J'apprécierais également des commentaires pour cette approche. J'ajouterai les appels à destructor pour nettoyer les objets créés


3 commentaires

s'il vous plaît voir mon commentaire sur la question initiale.


Votre fonction getList () copie les cartes des membres (à la fois m_fooMapList et m_newFoo à chaque appel. Repensez votre conception: est-ce que (mentionné ci-dessus) que vous voulez ou un const ref ou juste des objets Foo correspondants dans ces cartes, lorsque vous passez des clés s. Cela étant dit, si votre code fonctionne , vous devez le publier dans codereview.stackexchange.com pour un examen plus approfondi, pas dans SO.


@JeJo: Merci, j'utilise C ++ 11 pour le moment. J'ai également ajouté la question à Code Review Stack Exchange - codereview.stackexchange.com/q/213904/147059