Вставить указатель объекта в карту карт через emplace () не работает - PullRequest
0 голосов
/ 20 февраля 2019

Я пытаюсь вставить объект указателя с map по emplace(), но он не работает.

Я создал простое представление проблемы ниже.Я пытаюсь вставить в newFooList указатель типа объекта Foo*.

Кажется, я не могу найти способ создать тип для FooMap* в std::map<int, FooMap*> m_fooMapList.Должно ли это быть сделано с new во втором поле карты?

#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;
}

На g ++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28)

$  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;

Ответы [ 5 ]

0 голосов
/ 20 февраля 2019

Я рассмотрел правильные точки в каждом из ответов, чтобы удалить указатели и удалить ненужные двухуровневые представления карт.Но абстракция реального мира - это очень сложная проблема, которая включает в себя тысячи объектов на лету, которые необходимо динамически создавать и уничтожать.Использование указателей казалось верным подходом, но подход JeJo казался намного лучше.

Я пытался повторно использовать его попытку, но с указателями объектов, и приведенное ниже, похоже, работает.С приведенными ниже функциями вставки

В классе FooMap эта функция будет

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

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

, а в FooMapList будет

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;
}

Я был бы признателенобратная связь для этого подхода тоже.Я буду добавлять вызовы деструктора для очистки созданных объектов

0 голосов
/ 20 февраля 2019

Вы можете отбросить свои классы карты, которые ничего не делают, прекратить использовать указатели и просто

#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;
}

Обратите внимание, что внутренняя карта совершенно бессмысленна на данном этапе, так как онитолько одна запись

0 голосов
/ 20 февраля 2019

m_fooMapList определяется как

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

Поэтому для вставки в него необходим int и указатель на FooMap:

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

Сказав это, вы должны использовать семантику значений C ++ и меньше полагаться на необработанные указатели:

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

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

То есть экземпляры FooMap могут находиться непосредственно в самой карте.

Таким образомВы получаете лучшую производительность и избегаете утечек памяти.

Стоит также изучить интеллектуальные указатели (например, unique_ptr), если вы действительно хотите работать с указателями.

0 голосов
/ 20 февраля 2019

Я не уверен, нужна ли вам структура карты, где значения являются указателями на другую карту.Класс FooMapList может быть простым

std::map<int, FooMap> m_fooMapList;

С другой стороны, вся игра с указателем строки не принесет вам ничего, кроме боли в шее.

В случае использованияstd::map<int, FooMap*> m_fooMapList; и std::map<int, Foo*> являются необходимыми, я бы пошел для smartpointers.

Ниже приведен пример кода с заменой указателей строк на std::unique_ptr и показывает, как вставить карту Foo с карта на месте. Смотрите в прямом эфире здесь

#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;
}

Выход :

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...
0 голосов
/ 20 февраля 2019

За исключением случаев, когда у вас есть действительно веская причина для этого, избегайте запутывания таких вещей, как Java, как это, и старайтесь использовать STL так, как можете.Для этого вы можете использовать псевдонимы типа

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

Теперь у вас есть элемент Foo, который вы только что создали, и вы хотите вставить его в список карт, для этого вам нужен способ выбрать, в какомкарту в списке вы хотите вставить его.Я предполагаю, что вы вставите в первую карту в списке:

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
    );
}

Обратите внимание, что я не занимался очисткой памяти.Я предлагаю вам попробовать использовать умные указатели, когда это применимо (std::unique_ptr и std::shared_ptr)

...