C++: как сделать попроще?

0000

Имеется класс, объекты этого класса сейчас складируются в stl:vector (хранятся указатели на объекты).
Хочется завести для каждого объекта завести целочискленную характеристику и чтобы вставка в вектор (лист?) проходила с упорядочиванием по этой характеристике, т.е. когда будет пробегаться набор указателей от первого к последнему - они все будут упорядоченны по этой характеристике (возрастание).
P.S. Значения характеристики для двух объектов могут совпадать.

oliver11

std::map<характеристика, указатель на объект>

nikita270601

P.S. Значения характеристики для двух объектов могут совпадать.

oliver11

Ой. :-) Тогда multimap. :-)

Maurog

псевдо-код:

struct XComparer
{
bool operatorconst X* s1, const X* s2) const
{
return s1->Key - s2->Key < 0;
}
};

std::multiset<X*, XComparer> storage;

Key - целочисленная характеристика

0000

То есть указатели на объекты надо складировать в multiset с таким compare?

Papazyan

Чувак, stl не предназначена для хранения указателей, а уж map/multimap тем более.

0000

Ему разве не пофиг что хранить то?
P.S. Я С++ плохо знаю, просто для себя немного кодю.

slonishka

Чувак, stl не предназначена для хранения указателей, а уж map/multimap тем более.
эээ... ты не совсем прав, кстати! =)

Oper

Чувак, stl не предназначена для хранения указателей, а уж map/multimap тем более.
lol

Papazyan

Ему разве не пофиг что хранить то?
Нет, STL спроектирована для объектов, а не указателей. Накрайняк, можно умные указатели хранить.

slonishka

STL спроектирована для людей, которые сами могут решить, что им нужно хранить.

Papazyan

эээ... ты не совсем прав, кстати! =)
В случае map я имел в виду, что если ключ - указатель, то будет полная жопа.

slonishka

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

Papazyan

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

slonishka

буду сразу про бабки говорить, как ты в джобе учишь.

kokoc88

В случае map я имел в виду, что если ключ - указатель, то будет полная жопа.
На самом деле можно, хотя смысла обычно нет. Думаю, что так программировать небезопасно.
template <
class Key,
class Type,
class Traits = less<Key>,
class Allocator=allocator<pair <const Key, Type> >
>
class map

Papazyan

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

SCIF32

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

slonishka

разыменование делать в критерии эквивалентности никто не мешает.

Papazyan

буду сразу про бабки говорить, как ты в джобе учишь.
+1 Но только после того, как правильно повесишь лапшу на уши интервьюеру.

slonishka

чем умнее указатели, тем толще лапша?

SCIF32

Нельзя указать функцию для определения равенства. Если объекты эквивалентны, но место в памяти у них разное, то map не поймет по указателю, что такой ключ уже есть.
Compare в мапе для этого и сделано, что равенство через него считается.
Задаешь compare и сравниваешь содержимое объектов вместо указателей.

kokoc88

разыменование делать в критерии эквивалентности никто не мешает
Беда в том, что если это сырые указатели, то объекта может уже и не быть. Если настаиваешь - надо использовать boost/smart_ptr

slonishka

да это понятно. нужно просто либо быть аккуратным, либо, действительно, пользоваться умными указателями.

SCIF32

В классической доке даже пример есть
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 который проверяет значения.
Надеюсь я говорю о том же, о чем и вы?

Papazyan

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

slonishka

для сравнения в ассоциативных контейнерах STL используется оператор <

slonishka

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

SCIF32

задается операция <= а не <
update - задается именно <
равенство проверяется так (по крайней мере, где-то я читал об этом, давно, но не помню где):
== это когда не A>B и не B<A

slonishka

в STL используется < и это факт. =)
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]
а как реализовать бинарное дерево, имея только == — это загадка! =)

SCIF32

а точняк, ступил
почему-то зациклило, что на < равенство не построить
хотя там все почти тоже самое

Maurog

так точнее:
== это когда не A<B и не B<A

kokoc88

В классической доке даже пример есть
Эту доку писал Сишник. Так делать нельзя. Я могу туда же запихать std::string, c_str, а затем убить строку.

Dasar

> Так делать нельзя. Я могу туда же запихать std::string, c_str, а затем убить строку.
стоит применять аккуратно и делать нельзя - это совсем разные вещи.
если этот map живет локально внутри функции - то проблем нет.
если map живет внутри объекта, и этот же объект определяет время жизни элементов запиханный в map - то опять же проблем с указателями нет.

kokoc88

стоит применять аккуратно и делать нельзя - это совсем разные вещи
Я точно так же скажу, что нельзя писать some_clas* pclass. Потому что потом подобные тебе наезжают на Си++ из-за того, что сталкивались с кодом, где кто-то делает как делать нельзя. И прямо сейчас приведу кучу примеров, почему даже аккуратное использование таких вещей ведёт потом к проблемам.

slonishka

тем не менее, вот с этим я полностью согласен:
если этот map живет локально внутри функции - то проблем нет.
если map живет внутри объекта, и этот же объект определяет время жизни элементов запиханный в map - то опять же проблем с указателями нет.
если есть пример, когда это может привести к проблемам, выкладывай. интересно. =)

sbs-66

Например?

kokoc88

если есть пример, когда это может привести к проблемам, выкладывай
У тебя рано или поздно ситуация может выродиться в описанную мною выше. Даже в локальной функции могут быть проблемы с жизнью объектов.

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, а в Си++ нельзя работать с сырыми указателями (если пишется прикладной код).

kokoc88

Например?
Вот ещё ситуация из жизни: один программист написал некоторую функцию, которая должна была записывать в буфер. По непонятным причинам он написал внутри:

char* buf = new char[MAX_BUF];
........ // тут была куча кода
delete buf;

Всё было хорошо. Только месяцев через пять ему приспичило добавить в кучу кода что-то ещё. Это что-то кидало исключения, и в результате текла память.

Dasar

если переписать так, то будет лучше ?:

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) с непонятным временем жизни результата

kokoc88

если переписать так, то будет лучше ?:
Я тебе и с сырым указателем это сделаю: char* psz = new char[16], strcpy(psz, "test" delete psz
И второй свой пример я тоже перепишу на сырой указатель, типа char buf[16], strcpy(buf, "test" strcat(buf, "1")
проблема данного кода
Проблема данного подхода в целом в том, что так делать нельзя. Пример с сайта - плохой. Этим всё сказано.

slonishka

второй пример неплохой. но все равно уж слишком неаккуратный.
в принципе, он иллюстрирует скорее то, что map<указатель, value> действительно редко когда бывает нужен.
потому что проблема тут не от того, что указатель на объект в качестве ключа — это очень плохо,
а от того, что есть тип std::string, который умеет отдавать дескриптор своего внутреннего представления.
отдавать дескриптор внутреннего представления тоже нужно очень аккуратно, а уж тем более — засовывать его в контейнер.
потому что контейнеры — это копирование, а дескрипторы представления — это ссылки.
аккуратность тут заключается в понимании этой разницы имхо. =)

kokoc88

что есть тип std::string, который умеет отдавать дескриптор своего внутреннего представления

local_map["test1"] = 1;
char s[16] = "test";
local_map[s] = 3;
strcat(s, "1"); // передали куда угодно... код может достигать сотни строк и правиться через месяцы после написания оригинала
std::cout << local_map["test1"] << std::endl;

slonishka

И второй свой пример я тоже перепишу на сырой указатель, типа char buf[16], strcpy(buf, "test" strcat(buf, "1")
вроде ж, если std::map<const char*, ...>, то такое не скомпилируется.
зы. идею я понял. пример хороший. =)

kokoc88

вроде ж, если std::map<const char*, ...>, то такое не скомпилируется
Проверь: void f(const int n) { } void g { int n; f(n); }

slonishka

да, скомпилилось, сцуко.

SCIF32

Эту доку писал Сишник. Так делать нельзя. Я могу туда же запихать std::string, c_str, а затем убить строку.
Мы здесь соревнуемся в том, как сломать и потерять два стальных шара что-ли? =)
Делать можно, только думать надо. Но это вроде обязательное условие при написании любой программы.
С таким подходом можно и переменной пользоваться, при этом в одном месте считая что она постоянна, а в другом ее изменять. Что это докажет - что вообще программы писать нельзя?

slonishka

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

kokoc88

Что это докажет - что вообще программы писать нельзя?
Конкретно твой пример доказывает, что так писать нельзя. Есть примеры, как писать можно. От ошибок это тебя не спасёт, зато спасёт от глупостей.

SCIF32

Твой пост - какая-то каша. Ни одной конкретной мысли.

kokoc88

Твой пост - какая-то каша. Ни одной конкретной мысли.
Это твои проблемы.

SCIF32

Просто получается так, что программист написал плохой код из-за того, что пример плохой.
Хотя дело не в примере, а в программисте.
Пример был элементарный, при этом рабочий и не вызывал утечки. Но пришел программист что-то там себе надумал и получил отстой.
Если программист не знает, что будет с c_str или со строчкой, указатель на которую он передает в map, то очевидно не стоит так делать. А если он всеже сделал - то сам виноват.

kokoc88

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

Если программист не знает, что будет с c_str или со строчкой, указатель на которую он передает в map, то очевидно не стоит так делать. А если он всеже сделал - то сам виноват.
Сразу видно начинающего. Думаешь, что сможешь держать мегабайты кода в голове в течении нескольких лет? Ты плохо ознакомился с последним примером: там написано, что строк дофига и вообще поменять решили через несколько месяцев. Если бы учебный пример был сразу правильным, то никаких проблем не возникло бы. Но пример неправильный, то есть сделан так, как писать программы на Си++ нельзя.

Oper

Но пример неправильный, то есть сделан так, как писать программы на Си++ нельзя.
А что сделал разработчик C++, чтобы запретить писать так, как нельзя ? Правильно, ничего не сделал, все только продолжает усложнять язык.

kokoc88

А что сделал разработчик C++, чтобы запретить писать так, как нельзя ?
Запретить никто никому ничего не может. Я написал примеры, которые показывают, почему так писать нельзя. Вопросы как можно мы здесь не обсуждаем. Всем более-менее очевидно, что с ключом std::string будет меньше ошибок.

Oper

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

mkrec

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

Oper

си. на худой конец, пусть используют сишные либы в программах на c++

oliver11

Может, тем, кто хочет такие вещи, стоит начать писать на Java?

slonishka

> А что сделал разработчик C++, чтобы запретить писать так, как нельзя?
> Правильно, ничего не сделал, все только продолжает усложнять язык.
Что это за "разработчик C++", который продолжает усложнять язык?
То что, так писать нельзя, сказал , который, насколько мне известно,
разработчиком языка не является (хоть и преуспел в изучении его тонкостей).
"Нельзя" — слишком эмоциональное слово в данном случае.
Конечно, на примере той же мозиллы можно видеть, к чему приводит кодирование в таком стиле в крупных проектах.
С другой стороны, если приложение небольшое по размеру, вполне можно обойтись своими силами в слежении за памятью и может быть сильно увеличить производительность, перестав проверять инварианты во время выполнения. А если требуется широкая переносимость, использование boost вполне может оказаться недопустимо.

Oper

навеяно вот этим

slonishka

хз.
пользователи boost, думаю, не увидят в C++0x ничего сложного.
а кто boost не пользуется, могут в старом стиле писать — никто не мешает.
а что там такого ужасного-то есть? я вот даже пару полезных для себя вещей нашел.
ну и указатели поумнеют как-никак.

oliver11

Далеко не всё, что написано на википедии, примут. Рекомендую, кстати, послушать интервью Страуструпа на тему будущего стандарта:
Initializer lists, автоопределение типа переменной - полезные вещи, которые не усложняют язык, но делают жизнь приятнее и логичнее.
Да и от сокетов в streams library и регулярных выражений в стандартной библиотеке тоже никому хуже не будет.

SCIF32

Сразу видно начинающего. Думаешь, что сможешь держать мегабайты кода в голове в течении нескольких лет? Ты плохо ознакомился с последним примером: там написано, что строк дофига и вообще поменять решили через несколько месяцев. Если бы учебный пример был сразу правильным, то никаких проблем не возникло бы. Но пример неправильный, то есть сделан так, как писать программы на Си++ нельзя.
Ну я понял, что в том, что программист написал быдлокод, виноват пример.
Только советую все-таки задуматься - пример, который я привел:
1. Был не про char* и не про что-либо еще. Пример про то, как писать свой compare для map.
2. Это довольно старый и краткий гайд по STL, он написан для людей, знающих C и C++, по крайней мере базу.
3. Никто не обещал, что для идиотов в нем будет разжевана каждая строчка кода.
Как ни старался, я не нашел ни одного намека в этом примере, что когда я пишу программу, мне следует создать чаровский буфер, продалбать его, а потом через несколько месяцев удивляться утечкам памяти и обвинять всех вокруг в непрофессионализме, но не себя самого.
ЗЫ
Хотя, да. Я думаю для тебя конкретно это плохой пример. Нужно было привести что-нибудь по-проще.

Papazyan

навеяно вот этим
Это будет гвоздем в крышку гроба С++. Никто, кроме невъебенных С++ дрочеров, не сможет это запомнить.

kokoc88

Это будет гвоздем в крышку гроба С++. Никто, кроме невъебенных С++ дрочеров, не сможет это запомнить.
Запомнить что? И зачем? Кто-то отменил документацию?.. Вон в C# ключевых слов больше, чем в Си++, и ничего - цветёт и пахнет.

kokoc88

Ну я понял, что в том, что программист написал быдлокод, виноват пример.
Речь была не о том, что написал какой-то программист. А о том, что нельзя писать так, как в этом примере. Кроме того, я детально расписал, почему.
Это довольно старый и краткий гайд по STL, он написан для людей, знающих C и C++, по крайней мере базу.
Знающим Си++ людям не нужен такой гайд, а новичкам он повредит. После таких примеров приходится объяснять, зачем нужно использовать stringstream вместо sprintf, почему не страшно catch(... чем полезны шаблоны, для чего на самом деле нужны деструкторы, почему исключения не тормозят программу и так далее.
Хотя, да. Я думаю для тебя конкретно это плохой пример. Нужно было привести что-нибудь по-проще.
Весьма глупая попытка наезда. Впрочем, бог с ним. Против моих доводов о том, что приведённый тобой пример учит плохому ты всё равно не попрёшь.

SCIF32

Речь была не о том, что написал какой-то программист. А о том, что нельзя писать так, как в этом примере. Кроме того, я детально расписал, почему.
Неужели, где именно ты расписал доводы?
В том то и проблема, что доводов нет - одно сплошное твое отношение к языкам С и С++.
Я не видел ни одного довода, кроме примеров про каких-то программистов, что-то там написавших.
Более того, ты даже не написал о том, что имеешь ввиду под тем, что "так писать нельзя". Хотя повторял это каждый раз.
Ты упомянул выше о том, что плохо писать classname *var, но в том примере ни одного такого объявления нету.
Укажи что-ли строчки кода из примера, которые ты имеешь ввиду.

kokoc88

Неужели, где именно ты расписал доводы?
В том то и проблема, что доводов нет - одно сплошное твое отношение к языкам С и С++.
Я привёл простой для понимания код, в котором описаны проблемы, к которым рано или поздно приведёт подход, расписанный в твоём примере. Если для тебя возможные проблемы проекта в будущем не важны, то этот код тебя вряд ли в чём-то убедит. Впрочем, когда и если ты наберёшь солидную практику разработки, ты изменишь своё мнение.
что имеешь ввиду под тем, что "так писать нельзя"

Под тем, что "так писать нельзя" я имею ввиду, что так писать нельзя.

SCIF32

Я привёл простой для понимания код, в котором описаны проблемы, к которым рано или поздно приведёт подход, расписанный в твоём примере.
В примере, что я привел никакого подхода не расписано.
При всем при том, было написано выше - зачем этот пример и к чему он относится.
Под тем, что "так писать нельзя" я имею ввиду, что так писать нельзя.

Так что, получается ты выдумал какой-то подход, утверждаешь что он к чему-то приведет и говоришь что это плохо, не приводя никаких аргументов. Молодец, так держать.

kokoc88

При всем при том, было написано выше - зачем этот пример и к чему он относится.
Я тебе на это уже отвечал. Знающим Си++ этот пример на фиг не нужен. Новичкам только вредит. И написал, почему.
Так что, получается ты выдумал какой-то подход, утверждаешь что он к чему-то приведет и говоришь что это плохо, не приводя никаких аргументов. Молодец, так держать.

Я утверждал, что пример написан так, как писать нельзя. Ни больше, ни меньше. Почему так писать нельзя я тоже написал. У тебя есть ещё вопросы?

SCIF32

У тебя есть ещё вопросы?
Прости, но ты какой-то тупой.
Я ведь не задал ни одного вопроса в своем посте.

kokoc88

Прости, но ты какой-то тупой.
Я ведь не задал ни одного вопроса в своем посте.
Нечего больше сказать? Слив защитан.

SCIF32

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

Вообще сказать есть чего, но с этого момента за деньги.

kokoc88

Что-то объяснять человеку, который считает что говорит от лица "всех знающих С++" дело не благодарное.
Ты ничего мне тут не объяснял. Выше по теме я написал, почему пример плохой, почему так писать нельзя. На это ты ничего не ответил, кроме дешёвых наездов. Никакой аргументации против моих кусков кода ты не привёл, кроме "я так никогда не сделаю" и "если кто-то сделает такую ошибку - он сам дурак". Я тебе уже сказал, что большой проект ты будешь делать не один, и он будет далеко не такой компактный, как этот код. Я просто продемонстрировал проблемы, которые возникают у людей, которых "учат" на таких отстойных примерах.
вообще сказать есть чего, но с этого момента за деньги

Спасибо. Платить за дистилированную воду я не собираюсь. Тем более человеку, который не ответит и на половину моих вопросов по Си++.

SCIF32

500 на R050618312413

kokoc88

500 на R050618312413
Разве что только пожертвовать тебе на окулиста.

sbs-66

а в Си++ нельзя работать с сырыми указателями (если пишется прикладной код).
Я чего-то не поянл наверное, но в твоём коде проблема в том, что со строками работают как со свтроенным типом, а не как с буфером. Тебе никто не обещал, что "test1" == "test1". Для строк надо завести свой класс или пользоваться std::string или ещё чем-то готовым, и быть аккуратным. Но сырые указатели тут не причём. Надо просто следить за временем жизни объектов. Если у тебя нет такой привычки, т.к. ты привык писать на Java или C#, то ты просто не сможешь получить от C++ полной эффективности, на которую он способен, но можешь пользоваться умными указателями или владеющими обёртками всегда, даже когда это и не нужно делать.
То же касается и исключений. Если ты заводишь локальную переменную в коде по new, то будь добр следи за исключениями (это в любом случае полезно). Если же ты используешь код, который может кидаться исключенями, то ты должен это понимать и писать код безопасно - использовать владеющие указатели и ставить try catch вокруг опасных кусков. Если ты пишешь код, не думая об исключениях, а они могут возникнуть, то это твоя проблема, а не языка.
PS. Если исключение аварийное и программа в любом случае после него не продолжит работу, вроде исчерпания памяти или внутринние инварианты вдруг оказались не инвариантаи и вы не знаете что делать, то такие исключения отслеживать не обязательно. Но если вы лезете к файлу или используете стороннюю библиотеку и не готовы к исключению, то это ваша проблема.

sbs-66

А. Я кажется понял, что там предполагалось, что указатели будут разыменовываться и сравниваться компаратором. Тогда строка про "test" == "test" не актуальна. Но в любом случае проблема этого кода в том, что в ней используют строки как встроенный тип, т.е. не заботятся об их времени жизни.
В общем, с указателями работать можно, но надо следить за временем их жизни. Принимать указатель от объекта, который сейчас умрёт, а потом хранить - глупость. Надо просто понимать, что объект - это одно, а указатель на объект - это совсем другое, и наличие у тебя указателя не гарантирует, что объект жив. Надо имееть и сам объект тоже. А указатели - это просто способ быстро понять, что эта вот сущность и вон та сужность - это один и тот же объект, или способ быстро передавать способ доступа к объекту, которым владееш некоторая сущность, во внутренние механизмы, которые живут меньше, чем эта сущность. Т.е. надо пользоваться указателями с умом.

slonishka

да хрен с ним с компаратором, вроде. он там нормальные указатели сравнивает.
проблема, что там ключ меняют, а мапу не говорят.
вместо sprintf-a юзаю snprintf почти всегда для подобных задач. пока не подводил.
но у нас такой жести, как в примере с мапом в коде не бывает и модули маленькие.

kokoc88

Надо просто следить за временем жизни объектов. Если у тебя нет такой привычки, т.к. ты привык писать на Java или C#, то ты просто не сможешь получить от C++ полной эффективности, на которую он способен, но можешь пользоваться умными указателями или владеющими обёртками всегда, даже когда это и не нужно делать.
Вот после таких заявлений всякие кохтпы, ехидно ухмыляясь, начинают кричать про "ручную работу". У меня нет привычки следить за временем жизни объектов. Она пропала года два назад. За меня этим занимаются деструкторы.
Обёртками пользоваться надо всегда. Я уже написал в этой теме, почему. Ты можешь дописать код через много месяцев после написания оригинала. Всего один вызов функции, кидающей исключения при ошибках. Или новый return. Кроме того, профессиональный Си++ программист должен понимать, что использование обёрток не снижает эффективности программы даже на 5%.
То же касается и исключений. Если ты заводишь локальную переменную в коде по new, то будь добр следи за исключениями (это в любом случае полезно). Если же ты используешь код, который может кидаться исключенями, то ты должен это понимать и писать код безопасно - использовать владеющие указатели и ставить try catch вокруг опасных кусков.

Если ты пишешь в программе new, то в первую очередь ты должен заботиться о том, куда попадает результат. В прикладном коде он должен попадать в любой вид хранилища, кроме "сырого". Это автоматически сократит количество кода и избавит от всех проблем. Как минимум, не придётся писать delete перед каждым return. (А потом ещё ловить утечки, когда добавляется новый return.) А исключения вообще надо ловить только в тех местах, где они обрабатываются. Конструкция try/catch в Си++ не предназначена для освобождения ресурсов.
Если ты пишешь код, не думая об исключениях, а они могут возникнуть, то это твоя проблема, а не языка.

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

sbs-66

Вот после таких заявлений всякие кохтпы, ехидно ухмыляясь, начинают кричать про "ручную работу". У меня нет привычки следить за временем жизни объектов. Она пропала года два назад. За меня этим занимаются деструкторы.Обёртками пользоваться надо всегда. Я уже написал в этой теме, почему. Ты можешь дописать код через много месяцев после написания оригинала. Всего один вызов функции, кидающей исключения при ошибках. Или новый return. Кроме того, профессиональный Си++ программист должен понимать, что использование обёрток не снижает эффективности программы даже на 5%.
Ни нихрена не понял, что я написал. Никто ж не говорит, что надо ручками удалять объекты при выходе. Надо класть их во владеющие хранилища. Но это не мешает после этого передавать сырые указатели на те же объекты в механизмы, которые живут меньше, чем владеющее хранилище. Передавать же везде умные указатели глупо, ибо снижает эффективность.
Если ты пишешь в программе new, то в первую очередь ты должен заботиться о том, куда попадает результат. В прикладном коде он должен попадать в любой вид хранилища, кроме "сырого". Это автоматически сократит количество кода и избавит от всех проблем. Как минимум, не придётся писать delete перед каждым return. (А потом ещё ловить утечки, когда добавляется новый return.) А исключения вообще надо ловить только в тех местах, где они обрабатываются. Конструкция try/catch в Си++ не предназначена для освобождения ресурсов.
Ещё раз. Я не обязан использовать умные указатели. Я могу создать какой-то объект по new и положить во владеющее хранилище или удалить, главное чтобы всё это происходило в пределах одной функции и между созданием объекта и его удалением или сохранением во владеющем хранилище не было нетривиального кода, который может вызвать исключения. Если такой код есть, то можно положить объект во владеющий указатель (можно не очень умныйумный пока не будет принято решение, надо ли его клать в какое-то другое хранилище или надо удалить. Я ни разу не призываю вызывать вручную delete, я лишь говорю, что умные указатели иногда избыточны, можно пользоваться более простыми методами. Учёт количества ссылок не нужен в 99% случаев. В 90% случаев достаточно массива, хранящего указатели на объекты, и вызывающего delete при удалении объектов, и объекта, хранящего указатель на объект, вызывающего delete в деструкторе и позволяющего следать Detach, т.е. забрать указатель без удаления. Всё. Учёт ссылок нужен, если ты начинаешь проектировать межмодульное взаимодействие с непредсказуемым или сложно формализуемым временем жизни объектов.

kokoc88

Но это не мешает после этого передавать сырые указатели на те же объекты в механизмы, которые живут меньше, чем владеющее хранилище.
Таким образом ты всё равно нарвёшься на одну из проблем, описанных в этом треде. (Речь изначально шла о ключах в std::map)
Передавать же везде умные указатели глупо, ибо снижает эффективность.

Ты уже ушёл от изначального смысла темы, и говоришь о чём-то вроде auto_ptr p1, raw* p2 = p1.get raw->method Вернёмся к std::map и неправильному примеру глупого кренделя. Я утверждал, что так писать нельзя.
Я могу создать какой-то объект по new и положить во владеющее хранилище или удалить, главное чтобы всё это происходило в пределах одной функции и между созданием объекта и его удалением или сохранением во владеющем хранилище не было нетривиального кода, который может вызвать исключения.

Вместо того, чтобы следить за десятками условий, можно просто полагаться на деструкторы. Это же спасёт от проблем, когда код будет рефакториться, дописываться и исправляться.
Учёт количества ссылок не нужен в 99% случаев.
Оригинальная цифра. Ну не нужен, так не нужен. О подсчёте ссылок никто вообще до сих пор не говорил. (Ты даже отцитировал часть моего поста - там нет ничего про подсчёт ссылок.) Я говорил о том, что нельзя использовать "сырой" захват ресурсов, т.к. это ведёт к проблемам. Опять же, это уже далеко от темы и моего утверждения о том, что пример глупого кренделя учит плохому.

sbs-66

Таким образом ты всё равно нарвёшься на одну из проблем, описанных в этом треде. (Речь изначально шла о ключах в std::map)
Так что за проблема-то? Можно её на пальцах описать? Я, простите меня грешного, не пользую стандартной библиотекой, потому тонкости работы map из неё не знаю. Опиши какое поведение ожидается, а какое имет место, и почему это неправильно на твой взгляд?
Если ты хочешь иметь map от изменяющихся объектов, который неразличает разные экземляры объектов с одинаковыми значениями, то тебе надо на самом деле map от значений, а не от указателей. А то что в этом случае используются char*, а не CString или std::string является ошибкой. Но если тебе надо отличать экземпляры класса или объекты не меняются, то можно использовать в map сырые указатели. Только не на char, а на CString или std::string. Если объекты могут меняться или удаляться, то в компараторе нельзя указатели разименовывать. Если они неизменны, то можно. Если при написании кода тебе это сложно запомнить - пользуйся умными указателями с подсчётом ссылок (я так понял, ты именно это предлагаешь в качестве решения данной конкретной проблемы с map). Но другие могут действовать по обстоятельством, и, когда это возможно, получать более эффективный код.

kokoc88

Так что за проблема-то? Можно её на пальцах описать?

Свой пример глупый крендель назвал "классическим". Выше по теме я уже описал несколько проблем, почему этот пример нельзя называть классическим, и вообще сказал, что так писать нельзя. Аргументировал я это тем, что рано или поздно можно натолкнуться на проблемы: объект может быть удалён или изменён. Причём на проблемы можно натолкнуться уже тогда, когда такой вот "эффективный и правильный" код написан, и всего лишь добавляется или изменяется незначительная его часть.
если при написании кода тебе это сложно запомнить - пользуйся умными указателями с подсчётом ссылок

Господи, да не надо меня учить. Пойми ты, запомнить кусок кода на месяц-другой ещё можно. Но не факт, что другой человек или ты сам через пол года будешь всё это помнить. Если язык предлагает тебе решения без кучи условий и необходмости что-то запоминать и за чем-то следить, почему бы это не использовать? Набор правил запомнить проще, чем изменчивые условия внутри конкретной программы.
И уж тем более, в этом случае я не буду пользоваться указателем с подсчётом ссылок. Это опять приведёт к проблемам.

sbs-66

В общем могу сказать, что есть большой проект, которому уже больше десятка лет, который продаётся и не имеет утечек памяти, даже сертифицирован майкрософтом на совместимость с XP и Vista, но при этом в нём во многих местах используются сырые указатели. И никаких проблем это не вызывает.

kokoc88

И никаких проблем это не вызывает.
Ну вот если бы ты дописал "и не вызывало, и не будет вызывать", тогда я бы кинул в тебя камень.
Оставить комментарий
Имя или ник:
Комментарий: