zerone
Гость
|
|
« : 21-02-2009 08:46 » |
|
Не знает ли кто-нибудь, толковой документации по STL, где было бы чётко написано какие методы должен содержать класс, который мы используем для шаблона. Например, у нас есть структура, я хочу запихнуть её в список. enum Gender {male, female}; struct Member {string name, Gender gender, int age};
list <Member> members;
При компиляции происходят ошибки. Как я понял, потому что в Member должны наличествовать некоторые операторы, в частности *, <, ==... Вот и хочется узнать, что нужно конкретно для каждого из контейнеров STL.
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #1 : 21-02-2009 08:53 » |
|
Лог с ошибками в студию. Я вообще обычно пользуюсь ресурсом www.cplusplus.comЕсть еще книга "STL" Вообще я не помню каких-то специфичных требований к данным хранящимся в контейнере list
|
|
|
Записан
|
Странно всё это....
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #2 : 21-02-2009 09:19 » |
|
ошибка тут struct Member {string name, Gender gender, int age}; STL вообще ни при чём struct Member { string name; Gender gender; int age; };
|
|
|
Записан
|
|
|
|
zerone
Гость
|
|
« Ответ #3 : 21-02-2009 10:33 » |
|
Согласен. Но это я только так на форуме набрал. К сожалению в программе структура объявлена правильно и дело не в этом.
|
|
|
Записан
|
|
|
|
zerone
Гость
|
|
« Ответ #4 : 21-02-2009 10:36 » |
|
Вот ругань компилятора: MatchMaker.cpp: In member function ‘void MatchMaker::getBestMatches(std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::string, int)’: MatchMaker.cpp:16: ошибка: template argument for ‘template<class _Alloc> class std::allocator’ uses local type ‘MatchMaker::getBestMatches(std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::string, int)::Member’ MatchMaker.cpp:16: ошибка: при конкретизации ‘template<class _Alloc> class std::allocator’ MatchMaker.cpp:16: ошибка: некорректный аргумент шаблона 2 MatchMaker.cpp:16: ошибка: invalid type in declaration before ‘;’ token
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #5 : 21-02-2009 11:29 » |
|
uses local type Похоже кто-то пытается в шаблон засунуть класс/структуру объявленную внутри функции/метода. Кто бы это мог быть?
|
|
|
Записан
|
Странно всё это....
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #6 : 27-10-2009 05:00 » |
|
если использую std::map , я так понимаю, что контейнер индексирует ключ, а , следовательно, в любое время .find(ключ) очень быстро найдёт запись ? Или сначала надо сортировать ?
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #7 : 27-10-2009 06:54 » |
|
Леш, карты не могут быть переупорядочены. Да, find() имеет логарифмическую сложность поиска.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #8 : 27-10-2009 07:25 » |
|
>>>карты не могут быть переупорядочены.
ага, уже понял ) Да и метода sort нет
>>>Да, find() имеет логарифмическую сложность поиска.
а что это значит ? Всё таки быстро то есть ?
|
|
|
Записан
|
|
|
|
Вад
|
|
« Ответ #9 : 27-10-2009 07:56 » |
|
Алексей1153++, если коротко - то да, быстро Там же внутреннее представление - дерево (по-моему, красно-чёрное обычно). Поэтому спуск по дереву до нужного элемента при размере карты в N элементов происходит за время порядка log_2(N). С отсортированным массивом и бинарным поиском можно получить такую же скорость, если не учитывать затраты на собственно сортировку.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #10 : 28-10-2009 05:04 » |
|
имеется std::map<int,int> M; std::map<int,int>::iterator it;
с содержимым, скажем 1,1 (M.begin()) 2,4 5,3 7,3 9,1 (M.end()--)
мысленно пронумеруем номерами по порядку #0 1,1 (M.begin()) #1 2,4 #2 5,3 #3 7,3 #4 9,1 (M.end()--)
как этот порядковый номер определить ? Пытался так (скажем, для ключа 5) DWORD dwdNPP=-1; it=M.find(5); if(it!=M.end()) { dwdNPP=it-M.begin(); }
но тут одно но: оператор "-" не определён для итератора. Что тут можно сделать ? а будет ли правильным: dwdNPP=it._Ptr-M.begin()._Ptr;
? Добавлено через 54 минуты и 49 секунд:вот так вроде оно dwdNPP=(M.begin()._Ptr-it._Ptr)/2;
----------------------------- нет, фигня это - работает через раз, а в релизе ._Ptr вообще недоступен )
|
|
« Последнее редактирование: 28-10-2009 06:02 от Алексей1153 »
|
Записан
|
|
|
|
Вад
|
|
« Ответ #11 : 28-10-2009 06:07 » |
|
По-моему, номер в мапе за константное время определить не удастся. А за линейное - с помощью функции distance. #include <iterator> // ...
it=M.find(5); if(it!=M.end()) { dwdNPP=static_cast<DWORD>( std::distance(M.begin(), it)) ); }
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #12 : 28-10-2009 06:10 » |
|
понятно, спасибо
Добавлено через 82 дня, 11 часов, 1 минуту и 48 секунд: Имеется
std::map<LONG/*id*/,CString/*описание*/> ,
где строка может быть достаточно длинной. Чтобы не хранить всё в озу, хочется превратить мап в кеш , то есть если в мапе нет нужного id , то он вместе с описанием подгружается из БД и кладётся в мап. Вопрос: как содержимое map сдвигать а-ля FIFO, чтобы количество элементов не превысило некоего N ?
|
|
« Последнее редактирование: 18-01-2010 17:12 от Алексей1153 »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #13 : 18-01-2010 17:07 » |
|
Леш, сделай класс, хранящий данные в map. Я так же делал.
Храни id в отдельной структуре - очереди. При превышении размера очереди выталкивай первый id и удаляй соответствующий ему элемент из map.
Только коли это кеш, то надо какой-то алгоритм для сортировки очереди иметь. Например, при обращении к элементу его id следует удалить из очереди и добавить в ее конец.
|
|
« Последнее редактирование: 18-01-2010 17:08 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #14 : 18-01-2010 17:17 » |
|
Нет, ну просто написать класс-очередь, это не сложно (но долго), хочется ничего не меняя, использовать уже имеющийся мап. К примеру, добавить к описанию время добавления и при подгрузке удалять записи с наименьшим временем. struct X { CString d;/*описание*/ DWORD ticks; };
std::map<LONG/*id*/, X> myMap;
а как индексировать по ticks теперь ? мысль: пусть будет дополнительный мап. Надо обдумать ) std::map<LONG/*id*/,CString/*описание*/> myMap; std::map<DWORD /*тики*/, id> tickIndex;
Добавлено через 15 минут и 26 секунд:правда, с тиками тут не совсем красиво, если программа будет работать 2 месяца, то первыми из мапа стунут замещаться самые новые записи (когда DWORD переполнится). Применю SYSTEMTIME
|
|
« Последнее редактирование: 18-01-2010 17:33 от Алексей1153 »
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #15 : 18-01-2010 18:06 » |
|
Алексей1153++, при обращении к ID, перемещай его в начало (как свежий), тогда в конце очереди естественным образом окажутся самые неиспользуемые ID - их и удалить.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #16 : 18-01-2010 18:28 » |
|
Алексей1153++, при обращении к ID, перемещай его в начало (как свежий), тогда в конце очереди естественным образом окажутся самые неиспользуемые ID - их и удалить.
То есть, уже без map ? Или я тогда не понял. Я сейчас делаю реализацию с двумя мапами, как в предыдущем посте - пока нет разногласий с целью ) Добавлено через 2 часа, 34 минуты и 21 секунду:вот примерно такое безобразие выходит (не тестировал ещё - засыпаю... ) class CIdTextKeeper { enum{e_max_count=10000};
// id строка typedef std::map<int,CString> td_IdTextMap;
// время id typedef std::map<DWORD,int> td_TimeIdMap;
// id время typedef std::map<int,DWORD> td_IdTimeMap;
CCriticalSection m_cris; td_IdTextMap m_IdTextMap; td_TimeIdMap m_TimeIdMap; td_IdTimeMap m_IdTimeMap;
public: void SetTexts(const int Id,CString Text) { //засовывание в БД //... }
void GetTexts(const int IdToFind,CString* pText) { if(!pText)return;
CSingleLock(&m_cris,1);
//находим новое время DWORD dwdNewTime=0; if(m_TimeIdMap.size()) { td_TimeIdMap::iterator it=m_TimeIdMap.end(); it--; dwdNewTime=it->first+1;
//боремся с переполнением таймера if(!dwdNewTime) { m_IdTextMap.clear();
m_TimeIdMap.clear(); m_IdTimeMap.clear(); } }
//ищем id в кеше td_IdTextMap::iterator it=m_IdTextMap.find(IdToFind); if(it!=m_IdTextMap.end()) { //нашли *pText=it->second;
//обновляем время m_TimeIdMap.erase(m_IdTimeMap[IdToFind]); m_IdTimeMap.erase(IdToFind); } else { //освобождаем место для новой записи int IdToDel; for(;m_IdTextMap.size()>=e_max_count;) { //определяем самый "старый" id
IdToDel=m_TimeIdMap.begin()->second;
//и удаляем m_TimeIdMap.erase(m_TimeIdMap.begin());
m_IdTimeMap.erase(IdToDel); m_IdTextMap.erase(IdToDel); }
//добавляем запись c IdToFind в мапы из базы CString text;
//text << БазаДанных[IdToFind]
//новая запись *pText=text; //вставляем текст m_IdTextMap[IdToFind]=text; } //обновляем время m_TimeIdMap[dwdNewTime]=IdToFind; m_IdTimeMap[IdToFind]=dwdNewTime; } };
|
|
« Последнее редактирование: 18-01-2010 21:03 от Алексей1153 »
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #17 : 19-01-2010 05:43 » |
|
как я делал нечто подобное. 1. один std::map<unsigned long, > struct Element { std::string decription_; DWORD lastUpdate_; // время последнего обращения }
std::map<unsigned long, Element> cont;
2. размер не ограничено, НО //далее специфика моей задачи 2.1. есть timeout хранения 2.2. известно, что при разумном timeout, колличество элементов в очереди будет не слишком велико. 3. зачистка старых элементов выполняется в отдельном потоке. Цель: уменьшить всплески нагрузки посредине обработки запроса, т.е. что бы не возникало ситуации, когда вдруг время обработки запроса резко возрастало по отношению к среднему из-за зачистки кэша. НО // далее специфика моей задачи раз в несколько минут был краткий технологический период приостановки обработки запросов (это старый артефакт я тут не причем, просто он мне был удобен в моей задаче), в этот момент и происходила зачистка буфера иначе получаем, то от чего убегали. 4. с переполнением таймера я боролся вот так if (currentTime - elem.lastUpdate_ > TTL ) // тут ОДНО переполнение учитывается автоматически
твое решение тоже ничего я тут вижу такую проблему: 1. существует некий поток запрос, какие-то частые какие-то не очень 2. в конце очереди скопились те, что не слишком частые 3. в потоке запросов какой-то процент запросов, практически уникальных, т.е. ОЧЕНЬ редких, но их достаточно много 4. в итоге в конце очереди наблюдается некоторый дребезг, т.е. не слишком частые запросы часто и много вытесняются уникальными (очень редкими) и тут же возвращаются назад (получаем 2 обращения в БД), т.е. не смотря на то, что были закэшированы частые запросы, мы имеет очень много обращений к БД. в цифрах: 10% запросов уникально, 30% очень редкие (уникальных id среди них в соотношении 1/10), 30% редкие(уникальных id среди них в соотношении 1/30), 30% частые (уникальных id среди них в соотношении 1/100) и из-за 10% уникальных запросов, может получится, что каждое пускай 5-е обращение пойдёт в обход кэша тут нужно иметь или достаточно большой размер очереди для нивирования проблемы или дополнительный механизм контроля, например, TTL нужно, только немного модифицировать вот это: //освобождаем место для новой записи int IdToDel; for(;m_IdTextMap.size()>=e_max_count;) { //определяем самый "старый" id
IdToDel=m_TimeIdMap.begin()->second;
//и удаляем m_TimeIdMap.erase(m_TimeIdMap.begin());
m_IdTimeMap.erase(IdToDel); m_IdTextMap.erase(IdToDel); }
|
|
|
Записан
|
Странно всё это....
|
|
|
lapulya
Молодой специалист
Offline
|
|
« Ответ #18 : 19-01-2010 13:52 » |
|
LogRus, я бы даже развил тему, если часто запрашиваемые данные составляют очень малую часть от всех (так обычно и бывает )))) ), но при этом опрос уникальных данных производится по всей базе (а в кеше мы храним последние запрошенные), но начиная с какого-то размера кеша попасть в кеш при очередной выборке будет нереально (он будет забит последними уникальными данными)
|
|
|
Записан
|
С уважением Lapulya
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #19 : 19-01-2010 16:56 » |
|
LogRus, то есть, у меня не хватает, грубо говоря, периодического удаления "очень старых" записей (по некоему критерию). Например, раз в 5 минут можно очищать 2/3 части мапа старой, захватывая старую часть ?
Насчёт специфики моей задачи: показывается лог сообщений, у каждого сообщения приписана та длинная строка. Обычно на экране около 100 последних сообщений, если только кто-то не возьмётся прокручитвать список.
|
|
« Последнее редактирование: 19-01-2010 16:59 от Алексей1153++ »
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #20 : 20-01-2010 04:06 » |
|
1. неограниченный размер или достаточно большой размер 2. удаление по таймауту,а не 2/3 данных удаление по таймауту можно сделать по разному, можно налету, можно отложено (в отдельном потоке) почитай про что-нибудь на вроде MemCached. параметры я бы сделал или настраиваемыми или вычисляемыми на лету. LogRus, я бы даже развил тему, если часто запрашиваемые данные составляют очень малую часть от всех (так обычно и бывает )))) ), но при этом опрос уникальных данных производится по всей базе (а в кеше мы храним последние запрошенные), но начиная с какого-то размера кеша попасть в кеш при очередной выборке будет нереально (он будет забит последними уникальными данными)
иногда, требуется иметь не очищаемый кэш. Собственно у нас почти весь кэш БД такой (+ много логики по его актуализации при изменениях в БД)
|
|
« Последнее редактирование: 20-01-2010 04:09 от LogRus »
|
Записан
|
Странно всё это....
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #21 : 20-01-2010 04:13 » |
|
LogRus, таймаут то где хранить ? Как индексировать для быстроты поиска ? Ещё одим мап ? А , кстати, чем плохо просто обрезать старую часть? Ну, с 2/3 это я погорячился, ну , скажем взять первое и последнее время и пропорционально вычислить 30% и удалить это старое время. А при переполнении (оно, кстати, наступит не скоро - я же счётчик веду, а не тики достаю) можно просто очистить кеш полностью - притормаживание раз в 200 лет никто не заметит ))
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #22 : 20-01-2010 04:41 » |
|
такой пример: 1. запрос 1 прошла 1 секунда 2. запрос 2 прошло 2 секунда 3. запрос 3 прошло 3 секунда при этом запрос 1 приходит раз в секунду при этом запрос 2 приходит раз в 2 секунды при этом запрос 3 приходит раз в 3 секунды далее предположим, что обычно других запросов то и нет ты удаляешь 2/3 контейнера и получаешь слишком частые обращения в БД по запросам 2 и 3 где хранить? ну вроде у тебя уже есть решение имхо я бы сделал просто настройку, и если она будет не достаточной тогда двинусь дальше можно конечно в софтину вложить вместо 100 часов 10000 часов, она будет вся из себя, НО кому это действительно надо? Бизнес требования? Не думаю. Поэтому я бы делал итерациями с постепенным улучшением функционала.
|
|
|
Записан
|
Странно всё это....
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #23 : 20-01-2010 05:22 » |
|
У меня же нет штампа времени, у меня счётчик. Чем больше счётчик - тем моложе запись. Новое значение счётчика генерирую из последнего элемента m_TimeIdMap. То есть можно задать "интервал старости" и придётся связать его с максимальным размером мапа - e_max_count , например enum{e_old_distance=e_max_count*20/100}; тогда добавляем метод, который следует вызывать раз в N минут void DeleteTheOldest() { CSingleLock(&m_cris,1);
if(!m_TimeIdMap.size())return;
//самое "молодое" время DWORD dwdCurrTime=-1;
td_TimeIdMap::iterator it=m_TimeIdMap.end(); it--; dwdCurrTime=it->first;
//есть ли вообще чего удалять ? if(dwdCurrTime<e_old_distance)return;
//время, меньше которого удалять DWORD dwdTimeToDelFrom_including=dwdCurrTime-e_old_distance;
it=m_TimeIdMap.find(dwdTimeToDelFrom_including); if(it==m_TimeIdMap.end())return; it++;
//собственно, чистим td_TimeIdMap::iterator itdel=m_TimeIdMap.begin(); for(;;) { if(itdel==it)break; m_IdTextMap.erase(itdel->second); m_IdTimeMap.erase(itdel->second); itdel++; }
m_TimeIdMap.erase(m_TimeIdMap.begin(),it); }
|
|
« Последнее редактирование: 20-01-2010 05:40 от Алексей1153++ »
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #24 : 20-01-2010 07:07 » |
|
может вместо счётчика GetTickCount?
|
|
|
Записан
|
Странно всё это....
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #25 : 20-01-2010 07:32 » |
|
LogRus, а смысл ? Я всё равно обеспечиваю увеличение счётчика, а о переполнении можно не заботиться
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #26 : 20-01-2010 07:42 » |
|
смысл в то, что счётчик это удалённость элемента относительно друг друга в пространстве, а не во времени. некоторый всплеск активности по уникальным запросам может отодвинуть часто используемый запрос в конец очерди
|
|
|
Записан
|
Странно всё это....
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #27 : 20-01-2010 07:43 » |
|
не вижу разницы Добавлено через 33 минуты и 41 секунду:4. в итоге в конце очереди наблюдается некоторый дребезг, т.е. не слишком частые запросы часто и много вытесняются уникальными (очень редкими) и тут же возвращаются назад (получаем 2 обращения в БД),
блин, никак не могу понять, откуда 2 чтения из базы: даже если парочка другая (даже десятков) редких записей дёрнется наверх, часто используемые вряд ли вытеснятся в конце. А, кроме того, они (часты) вскоре снова перейдут наверх.
|
|
« Последнее редактирование: 20-01-2010 08:17 от Алексей1153 »
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #28 : 20-01-2010 09:05 » |
|
2 чтения: 1. читаем уникальный идентификатор 2. читаем вытесненный не вижу разницы
.....даже если парочка другая (даже десятков) редких записей дёрнется наверх ....
ну при таких потоках данных, в общем-то пополам но допустим поток 1000 запросов в секунду или 10000 при этом часто повторяющихся (не реже раз в 5 минут) всего 10% от общего числа запросов 5 минут это 60*5*1000 = 300 000 запросов получается, что в худшем случае между 2-мя обращения для одного и того же ключа в течении 5 минут может влезть сотня другая тысяч запросов которая вытеснит его из кэша если же ты используеся время, а не счётчик, то запрос не вытеснится
|
|
|
Записан
|
Странно всё это....
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #29 : 20-01-2010 09:17 » |
|
ну с этим понятно. Можно и с тиками переделать - одну строчку исправить то Но у меня нет 1000 запросов в секунду. Чаще - 0 запросов в час, и это не предел, но когда начинают листать лог (иногда нужно), то будет, ну, скажем, 100 строк в секунду перематываться. Потому то и жаль всё в озу хранить - оно так редко может понадобится, что просто неприлично озу забивать Добавлено через 17 минут и 24 секунды:ага... А как быть с тем, что с GetTickCount индекс в m_TimeIdMap может слегка повториться ? Мысль - эсли это произошло, то брать значение "время самой новой записи+1" , переполнение же в двух местах теперь надо учитывать
|
|
« Последнее редактирование: 20-01-2010 09:35 от Алексей1153 »
|
Записан
|
|
|
|
|