Как правильно использовать карту со смарт-указателями и пользовательскими классами в качестве ключа и значения - PullRequest
1 голос
/ 07 мая 2019

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

Это карта, которую я придумал:

std::map<std::unique_ptr<Team>, std::unique_ptr<std::vector<std::unique_ptr<Employee>>>> teams;

И это тестовый код, в котором я пытался добавить новую команду и члена к ней:

bool Company::addTeam(const std::string & projectName)
{
    teams.emplace(std::unique_ptr<Team>(new Team(projectName)), std::unique_ptr<std::vector<std::unique_ptr<Employee>>>(new std::vector<std::unique_ptr<Employee>>()));
    for (std::map<std::unique_ptr<Team>, std::unique_ptr<std::vector<std::unique_ptr<Employee>>>>::iterator it = teams.begin(); it != teams.end(); ++it) {
        std::cout << it->first.get()->getProjectName();
        it->second.get()->emplace_back(std::unique_ptr<Employee>(new Programmer("David", "Beckham", "9803268780", "Djoe Beckham", Employee::LevelThree, projectName)));
        std::cout << "\n" << it->second.get()->at(0)->toString();
    }


    return false;
}

Код работает нормально и я могу напечатать данные сотрудника, но после закрытия приложения выдает исключениеи Visual Studio открывает delete_scalar.cpp и вызывает точку останова в этом коде:

void __CRTDECL operator delete(void* const block) noexcept
{
    #ifdef _DEBUG
    _free_dbg(block, _UNKNOWN_BLOCK); // break point
    #else
    free(block);
    #endif
}

Я пытаюсь выяснить, что я делаю неправильно и как это исправить.У меня есть пустые деструкторы для всех классов Employees, если это как-то связано с проблемой.Если моя идея выглядит очень глупо и есть более простой способ достичь моей цели, пожалуйста, сообщите мне.Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 07 мая 2019

Ваша идея может сработать , если у вас не будет проблем в одном из ваших классов.Из-за недостающих частей сказать сложно.

Однако, это не тот путь !!

Карта предназначена для работы с индексом по значению.Типичный вариант использования - найти элемент с уже существующим ключом.Конечно, вы можете использовать указатели в качестве ключа, но указатель будет действовать как своего рода идентификатор без каких-либо полиморфных операций.

С другой стороны, unique_ptr предназначен для обеспечения уникального владения.Таким образом, существует только одна уникальная копия каждого значения указателя.Это делает его очень трудным для использования в качестве значения карты:

auto team2 = make_unique<Team>(); 
teams [team2] = make_unique<vector<unique_ptr<Employee>>>();  // doesn't compile

Приведенный выше код не компилируется, поскольку team2 является unique_ptr и не может быть скопирован в параметр индексации.Использование его для поиска или вставки элемента потребует его перемещения:

teams [std::move(team2)] = make_unique<vector<unique_ptr<Employee>>>(); //compiles
assert (team2); // ouch

Но после перемещения значение unique_ptr больше не находится в team2, которое теперь пусто, так как уникальный указатель находится включ карты и он уникален. Это означает, что вы никогда не найдете обратно добавленную команду .

Лучшие альтернативы?

Если вы действительно хотите использовать полиморфный указатель в качестве ключа карты, вы должны хотя бы использовать shared_ptr, чтобы несколько копийможет существовать в вашем коде.Но я бы посоветовал использовать значения только в качестве ключа

Теперь к части значения карты.Нет смысла делать unique_ptr из vector: сам вектор не является полиморфным, а векторы хорошо разработаны для копирования, перемещения и так далее.Кроме того, sizeof(vector<...>) мало даже для очень больших векторов.

Используйте vector<unique_ptr<Employee>>, если вы хотите, чтобы вектор на карте был владеть Employees, или vector<shared_ptr<Employee>>, если вы хотите поделиться содержимым вектора.

1 голос
/ 07 мая 2019

С точки зрения дизайна, один человек может иметь несколько ролей в разных командах одновременно.Это может быть другой класс Role, связывающий человека с командой.Ни одна команда, ни одна из ролей концептуально не владеют личными объектами, поэтому Role может использовать простые указатели для связи Person с Team.

...