[C++]Сложный объект в качестве ключа std::map
class MyClass
{
public:
unsigned int WordId;
...
bool operator<(const WordId object2)
{
return WordId < object2.WordId;
}
....
}
После подключения данной либы в другой проект, собираюсь использовать ассоциативный список:
#include <map>
void OtherClass::OtherFunc()
{
MyClass myClass = GetMyObject();
std::map<MyClass, int>::iterator iter = container.find(myClass);
}
И компилятор сражу же выдает ошибку:
Стоит заметить, что ключевое слово "const" в прототип оператора я добавил именно после этой ошибки.
Далее я решил переопределить оператор явно для двух операндов, и в хэдере "MyClass.h"(рядом с MyClass, ведь логично их рядом расположить) написал функцию:
{
return onj1.WordId < obj2.WordId;
}
После написания этой функции вообще начался дурдом. Начали появляться следующие ошибки:
Это было только два раза.
и
Эта ошибка указывала на "файлы.obj", которые соответствовали классам, где используется MyClass. Таких ошибок было прилично.
Я уже и "extern" помечал эту функцию, и засовывал в дерективы #ifndef - #define - #endif.
Единственный раз, когда у меня получалось скомпилировать программу - это когда я помещал перегрузку оператора с двумя операндами (bool operator<(const MyClass obj1, const MyClass obj2)) в тот файл, где идет работа с std::map.
Но я не считаю это законченным решением. Это что же, мне постоянно переопределять оператор сравнения при каждом обращении к MyClass?
Что скажете?
{
public:
MyClass(int val)
{
WordId = val;
}
bool operator<(const MyClass object2) const
{
return WordId < object2.WordId;
}
private:
unsigned int WordId;
};
int main()
{
MyClass myClass(10);
std::map<MyClass, int>container;
container[myClass] = 10;
std::map<MyClass, int>::iterator iter = container.find(myClass);
return 0;
}
Так компиляется.
А еще я для себя заметил, что лучше отдельно компилить сначала либу, а потом второй проект. Что-то у них не срастается, когда их одновременно компилишь.
В любом случае, у меня сейчас тоже компиляется.
Конечно константную. map же использует константные ключи. Вообще странно что подобный вопрос возник. ВСЕ методы, которые не изменяют состояние объекта должны ВСЕГДА описываться как константные. Это же основы!
Объекты, передаваемые в параметры функций по ссылке или указателю, тоже надо передавать как константные, если они не меняются внутри. И объекты надо передавать по ссылке, а не по значению. Иначе каждый раз создается временный объект и происходит копирование в него.
То есть:
И да, раз уж объекты используются в качестве элементов контейнеров (да и не только в этом случае) - нужно описать им конструктор копирования и оператор присваивания. Особенно если объект будет не совсем простой.
Не знал и понимал этого. Да наверное и сейчас не понимаю, зачем обязательно сообщать компилятору, что объект не меняется. И тем более мне не понятно, зачем функцию помечать константной. Что это изменит? Но обязательно учту на будущее.
Спасибо за советы, все приму к сведению.
Ну тебе бы понравилось, если бы у простого константного объекта ты бы не мог вызвать какой нибудь геттер например?
Если ты передаешь объект по ссылке или указателю - то во первых ты обезопасишь себя от потенциальной ошибки, если попытаешься внутри функции изменить константный объект. У тебя просто не соберется код в таком случае. Причем попытаться изменить его ты можешь совсем не сразу, а через несколько лет например, модифицируя чужую функцию. Во вторых - таким образом ты показываешь пользователю функции, что это входной параметр, который не будет меняться. И что туда можно не опасаясь передавать свои объекты.
Если только какой-нибудь извращенец не напишит что-то вроде этого:
void fun(const int& x) {
(*const_cast<int*>(&x))*= 2;
}
int main() {
int y = 1;
fun(y); //судя по прототипу fun, y не должна меняться
std::cout << y << std::endl; //ой..
return 0;
}
(*(int*)(&x))*= 2; //даже без const_cast
}