C++: как сделать попроще?
std::map<характеристика, указатель на объект>
P.S. Значения характеристики для двух объектов могут совпадать.
Ой. :-) Тогда multimap. :-)
struct XComparer
{
bool operatorconst X* s1, const X* s2) const
{
return s1->Key - s2->Key < 0;
}
};
std::multiset<X*, XComparer> storage;
Key - целочисленная характеристика
То есть указатели на объекты надо складировать в multiset с таким compare?
Чувак, stl не предназначена для хранения указателей, а уж map/multimap тем более.
P.S. Я С++ плохо знаю, просто для себя немного кодю.
Чувак, stl не предназначена для хранения указателей, а уж map/multimap тем более.эээ... ты не совсем прав, кстати! =)
Чувак, stl не предназначена для хранения указателей, а уж map/multimap тем более.lol
Ему разве не пофиг что хранить то?Нет, STL спроектирована для объектов, а не указателей. Накрайняк, можно умные указатели хранить.
STL спроектирована для людей, которые сами могут решить, что им нужно хранить.
эээ... ты не совсем прав, кстати! =)В случае map я имел в виду, что если ключ - указатель, то будет полная жопа.
другое дело, что это может быть не очень удобно,
но обсуждаемого в данном треде мапа проблемы с указателем-ключом не касаются.
STL спроектирована для людей, которые сами могут решить, что им нужно хранить.На собеседовании только это не говори, могут не так понять
буду сразу про бабки говорить, как ты в джобе учишь.
В случае map я имел в виду, что если ключ - указатель, то будет полная жопа.На самом деле можно, хотя смысла обычно нет. Думаю, что так программировать небезопасно.
template <
class Key,
class Type,
class Traits = less<Key>,
class Allocator=allocator<pair <const Key, Type> >
>
class map
тебе же никто не мешает указать свои правила сравнения.Нельзя указать функцию для определения равенства. Если объекты эквивалентны, но место в памяти у них разное, то map не поймет по указателю, что такой ключ уже есть.
STL спроектирована для людей, которые сами могут решить, что им нужно хранить.Неужели у вас в конторе ни разу не давали вакансию дерзкого программиста?
На собеседовании только это не говори, могут не так понять
разыменование делать в критерии эквивалентности никто не мешает.
буду сразу про бабки говорить, как ты в джобе учишь.+1 Но только после того, как правильно повесишь лапшу на уши интервьюеру.
чем умнее указатели, тем толще лапша?
Нельзя указать функцию для определения равенства. Если объекты эквивалентны, но место в памяти у них разное, то map не поймет по указателю, что такой ключ уже есть.Compare в мапе для этого и сделано, что равенство через него считается.
Задаешь compare и сравниваешь содержимое объектов вместо указателей.
разыменование делать в критерии эквивалентности никто не мешаетБеда в том, что если это сырые указатели, то объекта может уже и не быть. Если настаиваешь - надо использовать boost/smart_ptr
да это понятно. нужно просто либо быть аккуратным, либо, действительно, пользоваться умными указателями.
http://www.sgi.com/tech/stl/Map.html
struct ltstr
{
bool operatorconst char* s1, const char* s2) const
{
return strcmp(s1, s2) < 0;
}
};
int main
{
map<const char*, int, ltstr> months;
months["january"] = 31;
months["february"] = 28;
months["march"] = 31;
months["april"] = 30;
months["may"] = 31;
months["june"] = 30;
months["july"] = 31;
months["august"] = 31;
months["september"] = 30;
months["october"] = 31;
months["november"] = 30;
months["december"] = 31;
cout << "june -> " << months["june"] << endl;
map<const char*, int, ltstr>::iterator cur = months.find("june");
map<const char*, int, ltstr>::iterator prev = cur;
map<const char*, int, ltstr>::iterator next = cur;
++next;
--prev;
cout << "Previous (in alphabetical order) is " << (*prev).first << endl;
cout << "Next (in alphabetical order) is " << (*next).first << endl;
}
Там именно делается мап для char*, но с compare который проверяет значения.
Надеюсь я говорю о том же, о чем и вы?
Я ща посмотрю реализацию дерева, но то, что я уже вижу, указывает на то, что для сравнения используется оператор ==.
для сравнения в ассоциативных контейнерах STL используется оператор <
Нельзя указать функцию для определения равенства. Если объекты эквивалентны, но место в памяти у них разное, то map не поймет по указателю, что такой ключ уже есть.если речь идет об этой проблеме, то я не понимаю, как указатель на константу может помочь ее обойти.
теоретически, могут быть два различных указателя на две эквивалентные константы.
update - задается именно <
равенство проверяется так (по крайней мере, где-то я читал об этом, давно, но не помню где):
== это когда не A>B и не B<A
http://www.sgi.com/tech/stl/StrictWeakOrdering.html
InvariantsIrreflexivity f(x, x) must be false.а как реализовать бинарное дерево, имея только == — это загадка! =)
Antisymmetry f(x, y) implies !f(y, x)
Transitivity f(x, y) and f(y, z) imply f(x, z).
Transitivity of equivalence Equivalence (as defined above) is transitive: if x is equivalent to y and y is equivalent to z, then x is equivalent to z. (This implies that equivalence does in fact satisfy the mathematical definition of an equivalence relation.) [1]
почему-то зациклило, что на < равенство не построить
хотя там все почти тоже самое
== это когда не A<B и не B<A
В классической доке даже пример естьЭту доку писал Сишник. Так делать нельзя. Я могу туда же запихать std::string, c_str, а затем убить строку.
стоит применять аккуратно и делать нельзя - это совсем разные вещи.
если этот map живет локально внутри функции - то проблем нет.
если map живет внутри объекта, и этот же объект определяет время жизни элементов запиханный в map - то опять же проблем с указателями нет.
стоит применять аккуратно и делать нельзя - это совсем разные вещиЯ точно так же скажу, что нельзя писать some_clas* pclass. Потому что потом подобные тебе наезжают на Си++ из-за того, что сталкивались с кодом, где кто-то делает как делать нельзя. И прямо сейчас приведу кучу примеров, почему даже аккуратное использование таких вещей ведёт потом к проблемам.
если этот map живет локально внутри функции - то проблем нет.если есть пример, когда это может привести к проблемам, выкладывай. интересно. =)
если map живет внутри объекта, и этот же объект определяет время жизни элементов запиханный в map - то опять же проблем с указателями нет.
Например?
если есть пример, когда это может привести к проблемам, выкладывайУ тебя рано или поздно ситуация может выродиться в описанную мною выше. Даже в локальной функции могут быть проблемы с жизнью объектов.
void f
{
std::map<const char*, int, some_less> local_map;
{
local_map[std::string("test1").c_str] = 1;
local_map[std::string("test1").c_str] = 2;
}
std::cout << local_map["test1"] << std::endl;
}
А ещё может случиться вот так:
local_map["test1"] = 1;
std::string s("test");
local_map[s.c_str] = 3;
s += "1"; // передали куда-то s по ссылке
std::cout << local_map["test1"] << std::endl;
И такое бывает сплошь и рядом. Даже при аккуратном написании кода. Поэтому захват ресурса в C# нельзя писать без using/try/finally, а в Си++ нельзя работать с сырыми указателями (если пишется прикладной код).
Например?Вот ещё ситуация из жизни: один программист написал некоторую функцию, которая должна была записывать в буфер. По непонятным причинам он написал внутри:
char* buf = new char[MAX_BUF];
........ // тут была куча кода
delete buf;
Всё было хорошо. Только месяцев через пять ему приспичило добавить в кучу кода что-то ещё. Это что-то кидало исключения, и в результате текла память.
void f
{
std::map<smart_ptr<const char*>, int, some_less> local_map;
{
local_map[smart_ptr<const char *>(std::string("test1").c_str] = 1;
local_map[smart_ptr<const char *>(std::string("test2").c_str] = 2;
}
std::cout << local_map[smart_ptr<const char *>("test1")] << std::endl;
}
проблема данного кода не в сырых указателях, а в том что используется функция (c_str) с непонятным временем жизни результата
если переписать так, то будет лучше ?:Я тебе и с сырым указателем это сделаю: char* psz = new char[16], strcpy(psz, "test" delete psz
И второй свой пример я тоже перепишу на сырой указатель, типа char buf[16], strcpy(buf, "test" strcat(buf, "1")
проблема данного кодаПроблема данного подхода в целом в том, что так делать нельзя. Пример с сайта - плохой. Этим всё сказано.
в принципе, он иллюстрирует скорее то, что map<указатель, value> действительно редко когда бывает нужен.
потому что проблема тут не от того, что указатель на объект в качестве ключа — это очень плохо,
а от того, что есть тип std::string, который умеет отдавать дескриптор своего внутреннего представления.
отдавать дескриптор внутреннего представления тоже нужно очень аккуратно, а уж тем более — засовывать его в контейнер.
потому что контейнеры — это копирование, а дескрипторы представления — это ссылки.
аккуратность тут заключается в понимании этой разницы имхо. =)
что есть тип std::string, который умеет отдавать дескриптор своего внутреннего представления
local_map["test1"] = 1;
char s[16] = "test";
local_map[s] = 3;
strcat(s, "1"); // передали куда угодно... код может достигать сотни строк и правиться через месяцы после написания оригинала
std::cout << local_map["test1"] << std::endl;
И второй свой пример я тоже перепишу на сырой указатель, типа char buf[16], strcpy(buf, "test" strcat(buf, "1")вроде ж, если std::map<const char*, ...>, то такое не скомпилируется.
зы. идею я понял. пример хороший. =)
вроде ж, если std::map<const char*, ...>, то такое не скомпилируетсяПроверь: void f(const int n) { } void g { int n; f(n); }
да, скомпилилось, сцуко.
Эту доку писал Сишник. Так делать нельзя. Я могу туда же запихать std::string, c_str, а затем убить строку.Мы здесь соревнуемся в том, как сломать и потерять два стальных шара что-ли? =)
Делать можно, только думать надо. Но это вроде обязательное условие при написании любой программы.
С таким подходом можно и переменной пользоваться, при этом в одном месте считая что она постоянна, а в другом ее изменять. Что это докажет - что вообще программы писать нельзя?
особенно если чел, который не является аффтаром исходного кода пытается быстро заклеить какую-нибудь дыру.
Что это докажет - что вообще программы писать нельзя?Конкретно твой пример доказывает, что так писать нельзя. Есть примеры, как писать можно. От ошибок это тебя не спасёт, зато спасёт от глупостей.
Твой пост - какая-то каша. Ни одной конкретной мысли.
Твой пост - какая-то каша. Ни одной конкретной мысли.Это твои проблемы.
Хотя дело не в примере, а в программисте.
Пример был элементарный, при этом рабочий и не вызывал утечки. Но пришел программист что-то там себе надумал и получил отстой.
Если программист не знает, что будет с c_str или со строчкой, указатель на которую он передает в map, то очевидно не стоит так делать. А если он всеже сделал - то сам виноват.
Пример был элементарный, при этом рабочий и не вызывал утечки. Но пришел программист что-то там себе надумал и получил отстой.
Если программист не знает, что будет с c_str или со строчкой, указатель на которую он передает в map, то очевидно не стоит так делать. А если он всеже сделал - то сам виноват.Сразу видно начинающего. Думаешь, что сможешь держать мегабайты кода в голове в течении нескольких лет? Ты плохо ознакомился с последним примером: там написано, что строк дофига и вообще поменять решили через несколько месяцев. Если бы учебный пример был сразу правильным, то никаких проблем не возникло бы. Но пример неправильный, то есть сделан так, как писать программы на Си++ нельзя.
Но пример неправильный, то есть сделан так, как писать программы на Си++ нельзя.А что сделал разработчик C++, чтобы запретить писать так, как нельзя ? Правильно, ничего не сделал, все только продолжает усложнять язык.
А что сделал разработчик C++, чтобы запретить писать так, как нельзя ?Запретить никто никому ничего не может. Я написал примеры, которые показывают, почему так писать нельзя. Вопросы как можно мы здесь не обсуждаем. Всем более-менее очевидно, что с ключом std::string будет меньше ошибок.
Запретить никто никому ничего не может.Можно выбросить из языка конструкции, приводящие к созданию такого кода. Арифметику указателей, например, сишные тайп-касты. Сделать прозрачной семантику виртуальных функций, инстанциирования.
что тогда будут делать люди, которым надо написать удобный быстрый одноразовый код?
си. на худой конец, пусть используют сишные либы в программах на c++
Может, тем, кто хочет такие вещи, стоит начать писать на Java?
> Правильно, ничего не сделал, все только продолжает усложнять язык.
Что это за "разработчик C++", который продолжает усложнять язык?
То что, так писать нельзя, сказал , который, насколько мне известно,
разработчиком языка не является (хоть и преуспел в изучении его тонкостей).
"Нельзя" — слишком эмоциональное слово в данном случае.
Конечно, на примере той же мозиллы можно видеть, к чему приводит кодирование в таком стиле в крупных проектах.
С другой стороны, если приложение небольшое по размеру, вполне можно обойтись своими силами в слежении за памятью и может быть сильно увеличить производительность, перестав проверять инварианты во время выполнения. А если требуется широкая переносимость, использование boost вполне может оказаться недопустимо.
пользователи boost, думаю, не увидят в C++0x ничего сложного.
а кто boost не пользуется, могут в старом стиле писать — никто не мешает.
а что там такого ужасного-то есть? я вот даже пару полезных для себя вещей нашел.
ну и указатели поумнеют как-никак.
Initializer lists, автоопределение типа переменной - полезные вещи, которые не усложняют язык, но делают жизнь приятнее и логичнее.
Да и от сокетов в streams library и регулярных выражений в стандартной библиотеке тоже никому хуже не будет.
Сразу видно начинающего. Думаешь, что сможешь держать мегабайты кода в голове в течении нескольких лет? Ты плохо ознакомился с последним примером: там написано, что строк дофига и вообще поменять решили через несколько месяцев. Если бы учебный пример был сразу правильным, то никаких проблем не возникло бы. Но пример неправильный, то есть сделан так, как писать программы на Си++ нельзя.Ну я понял, что в том, что программист написал быдлокод, виноват пример.
Только советую все-таки задуматься - пример, который я привел:
1. Был не про char* и не про что-либо еще. Пример про то, как писать свой compare для map.
2. Это довольно старый и краткий гайд по STL, он написан для людей, знающих C и C++, по крайней мере базу.
3. Никто не обещал, что для идиотов в нем будет разжевана каждая строчка кода.
Как ни старался, я не нашел ни одного намека в этом примере, что когда я пишу программу, мне следует создать чаровский буфер, продалбать его, а потом через несколько месяцев удивляться утечкам памяти и обвинять всех вокруг в непрофессионализме, но не себя самого.
ЗЫ
Хотя, да. Я думаю для тебя конкретно это плохой пример. Нужно было привести что-нибудь по-проще.
навеяно вот этимЭто будет гвоздем в крышку гроба С++. Никто, кроме невъебенных С++ дрочеров, не сможет это запомнить.
Это будет гвоздем в крышку гроба С++. Никто, кроме невъебенных С++ дрочеров, не сможет это запомнить.Запомнить что? И зачем? Кто-то отменил документацию?.. Вон в C# ключевых слов больше, чем в Си++, и ничего - цветёт и пахнет.
Ну я понял, что в том, что программист написал быдлокод, виноват пример.Речь была не о том, что написал какой-то программист. А о том, что нельзя писать так, как в этом примере. Кроме того, я детально расписал, почему.
Это довольно старый и краткий гайд по STL, он написан для людей, знающих C и C++, по крайней мере базу.Знающим Си++ людям не нужен такой гайд, а новичкам он повредит. После таких примеров приходится объяснять, зачем нужно использовать stringstream вместо sprintf, почему не страшно catch(... чем полезны шаблоны, для чего на самом деле нужны деструкторы, почему исключения не тормозят программу и так далее.
Хотя, да. Я думаю для тебя конкретно это плохой пример. Нужно было привести что-нибудь по-проще.Весьма глупая попытка наезда. Впрочем, бог с ним. Против моих доводов о том, что приведённый тобой пример учит плохому ты всё равно не попрёшь.
Речь была не о том, что написал какой-то программист. А о том, что нельзя писать так, как в этом примере. Кроме того, я детально расписал, почему.Неужели, где именно ты расписал доводы?
В том то и проблема, что доводов нет - одно сплошное твое отношение к языкам С и С++.
Я не видел ни одного довода, кроме примеров про каких-то программистов, что-то там написавших.
Более того, ты даже не написал о том, что имеешь ввиду под тем, что "так писать нельзя". Хотя повторял это каждый раз.
Ты упомянул выше о том, что плохо писать classname *var, но в том примере ни одного такого объявления нету.
Укажи что-ли строчки кода из примера, которые ты имеешь ввиду.
Неужели, где именно ты расписал доводы?Я привёл простой для понимания код, в котором описаны проблемы, к которым рано или поздно приведёт подход, расписанный в твоём примере. Если для тебя возможные проблемы проекта в будущем не важны, то этот код тебя вряд ли в чём-то убедит. Впрочем, когда и если ты наберёшь солидную практику разработки, ты изменишь своё мнение.
В том то и проблема, что доводов нет - одно сплошное твое отношение к языкам С и С++.
что имеешь ввиду под тем, что "так писать нельзя"
Под тем, что "так писать нельзя" я имею ввиду, что так писать нельзя.
Я привёл простой для понимания код, в котором описаны проблемы, к которым рано или поздно приведёт подход, расписанный в твоём примере.В примере, что я привел никакого подхода не расписано.
При всем при том, было написано выше - зачем этот пример и к чему он относится.
Под тем, что "так писать нельзя" я имею ввиду, что так писать нельзя.
Так что, получается ты выдумал какой-то подход, утверждаешь что он к чему-то приведет и говоришь что это плохо, не приводя никаких аргументов. Молодец, так держать.
При всем при том, было написано выше - зачем этот пример и к чему он относится.Я тебе на это уже отвечал. Знающим Си++ этот пример на фиг не нужен. Новичкам только вредит. И написал, почему.
Так что, получается ты выдумал какой-то подход, утверждаешь что он к чему-то приведет и говоришь что это плохо, не приводя никаких аргументов. Молодец, так держать.
Я утверждал, что пример написан так, как писать нельзя. Ни больше, ни меньше. Почему так писать нельзя я тоже написал. У тебя есть ещё вопросы?
У тебя есть ещё вопросы?Прости, но ты какой-то тупой.
Я ведь не задал ни одного вопроса в своем посте.
Прости, но ты какой-то тупой.Нечего больше сказать? Слив защитан.
Я ведь не задал ни одного вопроса в своем посте.
Также, что-то объяснять человеку, который считает что говорит от лица "всех знающих С++" дело не благодарное.
Нечего больше сказать? Слив защитан.
Вообще сказать есть чего, но с этого момента за деньги.
Что-то объяснять человеку, который считает что говорит от лица "всех знающих С++" дело не благодарное.Ты ничего мне тут не объяснял. Выше по теме я написал, почему пример плохой, почему так писать нельзя. На это ты ничего не ответил, кроме дешёвых наездов. Никакой аргументации против моих кусков кода ты не привёл, кроме "я так никогда не сделаю" и "если кто-то сделает такую ошибку - он сам дурак". Я тебе уже сказал, что большой проект ты будешь делать не один, и он будет далеко не такой компактный, как этот код. Я просто продемонстрировал проблемы, которые возникают у людей, которых "учат" на таких отстойных примерах.
вообще сказать есть чего, но с этого момента за деньги
Спасибо. Платить за дистилированную воду я не собираюсь. Тем более человеку, который не ответит и на половину моих вопросов по Си++.
500 на R050618312413
500 на R050618312413Разве что только пожертвовать тебе на окулиста.
а в Си++ нельзя работать с сырыми указателями (если пишется прикладной код).Я чего-то не поянл наверное, но в твоём коде проблема в том, что со строками работают как со свтроенным типом, а не как с буфером. Тебе никто не обещал, что "test1" == "test1". Для строк надо завести свой класс или пользоваться std::string или ещё чем-то готовым, и быть аккуратным. Но сырые указатели тут не причём. Надо просто следить за временем жизни объектов. Если у тебя нет такой привычки, т.к. ты привык писать на Java или C#, то ты просто не сможешь получить от C++ полной эффективности, на которую он способен, но можешь пользоваться умными указателями или владеющими обёртками всегда, даже когда это и не нужно делать.
То же касается и исключений. Если ты заводишь локальную переменную в коде по new, то будь добр следи за исключениями (это в любом случае полезно). Если же ты используешь код, который может кидаться исключенями, то ты должен это понимать и писать код безопасно - использовать владеющие указатели и ставить try catch вокруг опасных кусков. Если ты пишешь код, не думая об исключениях, а они могут возникнуть, то это твоя проблема, а не языка.
PS. Если исключение аварийное и программа в любом случае после него не продолжит работу, вроде исчерпания памяти или внутринние инварианты вдруг оказались не инвариантаи и вы не знаете что делать, то такие исключения отслеживать не обязательно. Но если вы лезете к файлу или используете стороннюю библиотеку и не готовы к исключению, то это ваша проблема.
В общем, с указателями работать можно, но надо следить за временем их жизни. Принимать указатель от объекта, который сейчас умрёт, а потом хранить - глупость. Надо просто понимать, что объект - это одно, а указатель на объект - это совсем другое, и наличие у тебя указателя не гарантирует, что объект жив. Надо имееть и сам объект тоже. А указатели - это просто способ быстро понять, что эта вот сущность и вон та сужность - это один и тот же объект, или способ быстро передавать способ доступа к объекту, которым владееш некоторая сущность, во внутренние механизмы, которые живут меньше, чем эта сущность. Т.е. надо пользоваться указателями с умом.
проблема, что там ключ меняют, а мапу не говорят.
вместо sprintf-a юзаю snprintf почти всегда для подобных задач. пока не подводил.
но у нас такой жести, как в примере с мапом в коде не бывает и модули маленькие.
Надо просто следить за временем жизни объектов. Если у тебя нет такой привычки, т.к. ты привык писать на Java или C#, то ты просто не сможешь получить от C++ полной эффективности, на которую он способен, но можешь пользоваться умными указателями или владеющими обёртками всегда, даже когда это и не нужно делать.Вот после таких заявлений всякие кохтпы, ехидно ухмыляясь, начинают кричать про "ручную работу". У меня нет привычки следить за временем жизни объектов. Она пропала года два назад. За меня этим занимаются деструкторы.
Обёртками пользоваться надо всегда. Я уже написал в этой теме, почему. Ты можешь дописать код через много месяцев после написания оригинала. Всего один вызов функции, кидающей исключения при ошибках. Или новый return. Кроме того, профессиональный Си++ программист должен понимать, что использование обёрток не снижает эффективности программы даже на 5%.
То же касается и исключений. Если ты заводишь локальную переменную в коде по new, то будь добр следи за исключениями (это в любом случае полезно). Если же ты используешь код, который может кидаться исключенями, то ты должен это понимать и писать код безопасно - использовать владеющие указатели и ставить try catch вокруг опасных кусков.
Если ты пишешь в программе new, то в первую очередь ты должен заботиться о том, куда попадает результат. В прикладном коде он должен попадать в любой вид хранилища, кроме "сырого". Это автоматически сократит количество кода и избавит от всех проблем. Как минимум, не придётся писать delete перед каждым return. (А потом ещё ловить утечки, когда добавляется новый return.) А исключения вообще надо ловить только в тех местах, где они обрабатываются. Конструкция try/catch в Си++ не предназначена для освобождения ресурсов.
Если ты пишешь код, не думая об исключениях, а они могут возникнуть, то это твоя проблема, а не языка.
Не думают об исключениях только те, кто работает с ресурсами напрямую. Ещё раз повторю, что язык сам может думать об исключениях, и не надо делать ручную работу, в которой всё время обвиняют Си++сников (как видно, заслуженно).
Вот после таких заявлений всякие кохтпы, ехидно ухмыляясь, начинают кричать про "ручную работу". У меня нет привычки следить за временем жизни объектов. Она пропала года два назад. За меня этим занимаются деструкторы.Обёртками пользоваться надо всегда. Я уже написал в этой теме, почему. Ты можешь дописать код через много месяцев после написания оригинала. Всего один вызов функции, кидающей исключения при ошибках. Или новый return. Кроме того, профессиональный Си++ программист должен понимать, что использование обёрток не снижает эффективности программы даже на 5%.Ни нихрена не понял, что я написал. Никто ж не говорит, что надо ручками удалять объекты при выходе. Надо класть их во владеющие хранилища. Но это не мешает после этого передавать сырые указатели на те же объекты в механизмы, которые живут меньше, чем владеющее хранилище. Передавать же везде умные указатели глупо, ибо снижает эффективность.
Если ты пишешь в программе new, то в первую очередь ты должен заботиться о том, куда попадает результат. В прикладном коде он должен попадать в любой вид хранилища, кроме "сырого". Это автоматически сократит количество кода и избавит от всех проблем. Как минимум, не придётся писать delete перед каждым return. (А потом ещё ловить утечки, когда добавляется новый return.) А исключения вообще надо ловить только в тех местах, где они обрабатываются. Конструкция try/catch в Си++ не предназначена для освобождения ресурсов.Ещё раз. Я не обязан использовать умные указатели. Я могу создать какой-то объект по new и положить во владеющее хранилище или удалить, главное чтобы всё это происходило в пределах одной функции и между созданием объекта и его удалением или сохранением во владеющем хранилище не было нетривиального кода, который может вызвать исключения. Если такой код есть, то можно положить объект во владеющий указатель (можно не очень умныйумный пока не будет принято решение, надо ли его клать в какое-то другое хранилище или надо удалить. Я ни разу не призываю вызывать вручную delete, я лишь говорю, что умные указатели иногда избыточны, можно пользоваться более простыми методами. Учёт количества ссылок не нужен в 99% случаев. В 90% случаев достаточно массива, хранящего указатели на объекты, и вызывающего delete при удалении объектов, и объекта, хранящего указатель на объект, вызывающего delete в деструкторе и позволяющего следать Detach, т.е. забрать указатель без удаления. Всё. Учёт ссылок нужен, если ты начинаешь проектировать межмодульное взаимодействие с непредсказуемым или сложно формализуемым временем жизни объектов.
Но это не мешает после этого передавать сырые указатели на те же объекты в механизмы, которые живут меньше, чем владеющее хранилище.Таким образом ты всё равно нарвёшься на одну из проблем, описанных в этом треде. (Речь изначально шла о ключах в std::map)
Передавать же везде умные указатели глупо, ибо снижает эффективность.
Ты уже ушёл от изначального смысла темы, и говоришь о чём-то вроде auto_ptr p1, raw* p2 = p1.get raw->method Вернёмся к std::map и неправильному примеру глупого кренделя. Я утверждал, что так писать нельзя.
Я могу создать какой-то объект по new и положить во владеющее хранилище или удалить, главное чтобы всё это происходило в пределах одной функции и между созданием объекта и его удалением или сохранением во владеющем хранилище не было нетривиального кода, который может вызвать исключения.
Вместо того, чтобы следить за десятками условий, можно просто полагаться на деструкторы. Это же спасёт от проблем, когда код будет рефакториться, дописываться и исправляться.
Учёт количества ссылок не нужен в 99% случаев.Оригинальная цифра. Ну не нужен, так не нужен. О подсчёте ссылок никто вообще до сих пор не говорил. (Ты даже отцитировал часть моего поста - там нет ничего про подсчёт ссылок.) Я говорил о том, что нельзя использовать "сырой" захват ресурсов, т.к. это ведёт к проблемам. Опять же, это уже далеко от темы и моего утверждения о том, что пример глупого кренделя учит плохому.
Таким образом ты всё равно нарвёшься на одну из проблем, описанных в этом треде. (Речь изначально шла о ключах в std::map)Так что за проблема-то? Можно её на пальцах описать? Я, простите меня грешного, не пользую стандартной библиотекой, потому тонкости работы map из неё не знаю. Опиши какое поведение ожидается, а какое имет место, и почему это неправильно на твой взгляд?
Если ты хочешь иметь map от изменяющихся объектов, который неразличает разные экземляры объектов с одинаковыми значениями, то тебе надо на самом деле map от значений, а не от указателей. А то что в этом случае используются char*, а не CString или std::string является ошибкой. Но если тебе надо отличать экземпляры класса или объекты не меняются, то можно использовать в map сырые указатели. Только не на char, а на CString или std::string. Если объекты могут меняться или удаляться, то в компараторе нельзя указатели разименовывать. Если они неизменны, то можно. Если при написании кода тебе это сложно запомнить - пользуйся умными указателями с подсчётом ссылок (я так понял, ты именно это предлагаешь в качестве решения данной конкретной проблемы с map). Но другие могут действовать по обстоятельством, и, когда это возможно, получать более эффективный код.
Так что за проблема-то? Можно её на пальцах описать?
Свой пример глупый крендель назвал "классическим". Выше по теме я уже описал несколько проблем, почему этот пример нельзя называть классическим, и вообще сказал, что так писать нельзя. Аргументировал я это тем, что рано или поздно можно натолкнуться на проблемы: объект может быть удалён или изменён. Причём на проблемы можно натолкнуться уже тогда, когда такой вот "эффективный и правильный" код написан, и всего лишь добавляется или изменяется незначительная его часть.
если при написании кода тебе это сложно запомнить - пользуйся умными указателями с подсчётом ссылок
Господи, да не надо меня учить. Пойми ты, запомнить кусок кода на месяц-другой ещё можно. Но не факт, что другой человек или ты сам через пол года будешь всё это помнить. Если язык предлагает тебе решения без кучи условий и необходмости что-то запоминать и за чем-то следить, почему бы это не использовать? Набор правил запомнить проще, чем изменчивые условия внутри конкретной программы.
И уж тем более, в этом случае я не буду пользоваться указателем с подсчётом ссылок. Это опять приведёт к проблемам.
В общем могу сказать, что есть большой проект, которому уже больше десятка лет, который продаётся и не имеет утечек памяти, даже сертифицирован майкрософтом на совместимость с XP и Vista, но при этом в нём во многих местах используются сырые указатели. И никаких проблем это не вызывает.
И никаких проблем это не вызывает.Ну вот если бы ты дописал "и не вызывало, и не будет вызывать", тогда я бы кинул в тебя камень.
Оставить комментарий
0000
Имеется класс, объекты этого класса сейчас складируются в stl:vector (хранятся указатели на объекты).Хочется завести для каждого объекта завести целочискленную характеристику и чтобы вставка в вектор (лист?) проходила с упорядочиванием по этой характеристике, т.е. когда будет пробегаться набор указателей от первого к последнему - они все будут упорядоченны по этой характеристике (возрастание).
P.S. Значения характеристики для двух объектов могут совпадать.