V>[...] V>>>Я просто высказал свое мнение. Конкретная проблема — неотловленный эксепшин в деструкторе приводит к "undefined behaviour". SZ>>Влад, определись. То конструкторы, то деструкторы...
V>Одна фигня.
совершенно не одна фигня.
Вспомните доводы против выбрасывания исключений из деструкторов и попытайтесь хоть один из них применить к конструкторам.
Здравствуйте jazzer, Вы писали:
V>>Угу. Неотлавливаемая компилятором. Причем чем меньше источников эксепшинов — тем меньше вероятность ее возникновения. J>Логика железная
Только так.
J>а чем меньше указателей и работы с динамической помятью — тем меньше вероятность, что мы получим access violation.
Безусловно.
J>Это — аргумент в пользу отказа от указателей?
Да. Именно поэтому существуют более другие языки. В некоторых из них, кстати, и механизм эксепшинов реализован более правильно.
Здравствуйте jazzer, Вы писали:
J>совершенно не одна фигня. J>Вспомните доводы против выбрасывания исключений из деструкторов и попытайтесь хоть один из них применить к конструкторам.
Здравствуйте Bell, Вы писали:
V>>Я просто высказал свое мнение. Конкретная проблема — неотловленный эксепшин в деструкторе приводит к "undefined behaviour". B>Можно место в стандарте, где это написано?
Уел.
15.2.3. "...If a destructor called during stack unwinding exits with an exception, terminate is called...".
Здравствуйте Vladik, Вы писали:
V>Здравствуйте jazzer, Вы писали:
V>>>Угу. Неотлавливаемая компилятором. Причем чем меньше источников эксепшинов — тем меньше вероятность ее возникновения. J>>Логика железная :)
V>Только так.
J>>а чем меньше указателей и работы с динамической помятью — тем меньше вероятность, что мы получим access violation.
V>Безусловно.
J>>Это — аргумент в пользу отказа от указателей?
V>Да. Именно поэтому существуют более другие языки. В некоторых из них, кстати, и механизм эксепшинов реализован более правильно.
А почему мы, собственно, ограничиваемся памятью?
Вот, например, есть вероятность, что файловые операции уничтожат важные данные, находящиеся в других файлах, а то и попортят фаловую систему, если не приведут к неожиданному форматированию диска.
Почему бы не отказаться и от этого?
Все определяется кривизной рук разработчика.
Если человек в жизни не использовал эксепшны, а потом прочитал книжку "С++ за 21 час" и начал писать такой код, который Вы приводили в примерах, то это говорит не о том, что механизм (или язык) плохой, а о тот, что квалификация программера не дотягивает.
Да, еще, конечно, можно поговорить о том, что в java это "лучше/короче/толще/больше градусов" (с)
Здравствуйте jazzer, Вы писали:
J>>>Это — аргумент в пользу отказа от указателей? V>>Да. Именно поэтому существуют более другие языки. В некоторых из них, кстати, и механизм эксепшинов реализован более правильно. J>А почему мы, собственно, ограничиваемся памятью? J>Вот, например, есть вероятность, что файловые операции уничтожат важные данные, находящиеся в других файлах, а то и попортят фаловую систему, если не приведут к неожиданному форматированию диска. J>Почему бы не отказаться и от этого?
Не уловил я связи... И на VB можно запороть очень важные данные.
J>Все определяется кривизной рук разработчика. J>Если человек в жизни не использовал эксепшны, а потом прочитал книжку "С++ за 21 час" и начал писать такой код, который Вы приводили в примерах, то это говорит не о том, что механизм (или язык) плохой, а о тот, что квалификация программера не дотягивает.
Действительно железная логика
J>Да, еще, конечно, можно поговорить о том, что в java это "лучше/короче/толще/больше градусов" (с)
Exeption-ы очень удобны при "транзакционной" логике программы (т.е. если не выполнилось какое-то действие, то не надо выполнять и все последующие), а вот что делать, если хочется, чтобы функция продолжала работать дальше, даже если предыдущая часть кода сломалась:
При использование exception-ов получается следующий код:
void Save()
{
try
{
WriteLog("Save");
}
catch (...)
{
}
string filename = "out.dat";
try
{
filename = GetFileName();
}
catch (...)
{
}
File file (filename);
file.Write (data);
}
//А функция WriteLog выглядит как-нибудь такvoid WriteLog(string s)
{
File file ("log.dat"); //здесь
file.Write (s); //или здесь может выскочить exception
}
//А функция GetFileName() берет название файла, например, из конфигурационного файла:
string GetFileName()
{
File file ("config.dat"); //здесь опять же может выскочить exceptionreturn file.ReadLine();
}
p.s. Самое плохое, что ни в одном языке нет даже простейшего способа скипнуть exception...
Здравствуйте Vladik, Вы писали:
V>Здравствуйте Bell, Вы писали:
V>>>Я просто высказал свое мнение. Конкретная проблема — неотловленный эксепшин в деструкторе приводит к "undefined behaviour". B>>Можно место в стандарте, где это написано?
V>Уел.
V>15.2.3. "...If a destructor called during stack unwinding exits with an exception, terminate is called...".
V>Это решает проблему?
Ну теперь снято обвинение в "undefined behaviour"
Здравствуйте DarkGray, Вы писали:
DG>p.s. Самое плохое, что ни в одном языке нет даже простейшего способа скипнуть exception...
Это и самое хорошее. Про код возврата можно забыть а ексепшен пнет хочется этого или нет.
Другое дело ожидает ли программер что его пнут или нет
Я когда чего отлаживаю то в коде который может кинуть ексепшен я его кидаю насильственно — чтоб посмотреть где оно вылезет и что можно сделать дальше. В общем обрабатываю ситуацию по принципу ежли чего плохое может случится то оно случится обязательно.
Такие примеры ни чего не доказывают. Повторюсь, они лишь показывают, что логика ориентированная на структурное программирование плохо сочетается с механизмом исключений. К тому же система логирования вообще не должна кидать исключения, их надо прятать за фасадом, потому что из-за ошибки при записи лога приложение должно оставаться работоспособным.
Вот немного переписаный кусок кода:
void writeLog( const std::string& strLog )
{
try
{
File file ("log.dat");
file.Write (s);
}
catch(...)
{
// assertion для debug версии.
}
}
class Data
{
std::string getDataFileName()
{
std::string fileName = "out.dat";
try
{
fileName = ... //получение имени файла
}
catch(...)
{
writeLog("Exception when trying to get file name for data");
// assertion для debug версии.
}
return fileName
}
public:
void write( const Param& param )
{
File file( getDataFileName() )
file.write( param );
}
Param param read()
{
File file( getDataFileName() )
return file.read();
}
}
void save()
{
writeLog("Save");
try
{
Data data;
data.write();
}
catch(...)
{
writeLog("Exception in 'save' function");
}
}
void load()
{
writeLog("Load");
try
{
Data data;
data.read();
}
catch(...)
{
writeLog("Exception in 'load' function");
}
}
намеренно не стал детально прорабатывать производительность, поэтому при вызове writeLog постоянно создается и уничтожается объект класса File, не об этом сейчас разговор.
DG>p.s. Самое плохое, что ни в одном языке нет даже простейшего способа скипнуть exception...
и не надо. Не для этого они сделаны чтоб их скипали.
SZ>Такие примеры ни чего не доказывают. Повторюсь, они лишь показывают, что логика ориентированная на структурное программирование плохо сочетается с механизмом исключений. К тому же система логирования вообще не должна кидать исключения, их надо прятать за фасадом, потому что из-за ошибки при записи лога приложение должно оставаться работоспособным.
Что ты привязался к названию writeLog обзови ее как-нибудь по другому — write_неважные_данные или сделать_не_очень_важную_операцию....
SZ>Вот немного переписаный кусок кода:
То есть ты хочешь сказать, что я на все библиотечные классы кидающие исключения должен писать свои классы, которые исключения не кидают?
Допустим на миг, что функция writeLog — библиотечная, тогда такой вопрос на засыпку — она должна кидать исключение или нет, если нет, то как нам все-таки узнать, что она закончилась не удачно... Ну ладно добавили все-таки возвращающее значение, которое будет сигнализировать об ошибке.
Опять же, а если getDataFileName — библиотечная, то она должно кидать исключение или не должна, если не должна то как мы узнаем об ошибке, если нам это важно?
DG>>p.s. Самое плохое, что ни в одном языке нет даже простейшего способа скипнуть exception...
SZ>и не надо. Не для этого они сделаны чтоб их скипали.
А как писать устойчивые приложения, если не скипать исключения. Обидно, когда вся программа валится из-за того, что не получилось сделать какое-то левое действие.
Здравствуйте Vladik, Вы писали:
V>Ну и нахрена мне в релизе все эти try/catch, если все равно оно не юзается?
Кто сказал, что они не юзаются? Они говорят о том, что любая ошибка при записи лога (памяти не хватило, диск отвалился) не повлият на работу программы.
V>ofstream file("log.dat"); V> file<<s; V> assert(file);
V>Ну чем этот вариант хуже???
Тем, что в общем случае в release он будет валит всю программу (например, если file-у или operator-у << не хватило памяти)
Здравствуйте Sergey Zhulin, Вы писали:
SZ>Такие примеры ни чего не доказывают. Повторюсь, они лишь показывают, что логика ориентированная на структурное программирование плохо сочетается с механизмом исключений.
И в каком месте мой пример был ориентирован на структурное программирование?
Здравствуйте DarkGray, Вы писали:
DG>Ладно, еще один пример — вот я пишу свою функцию atoi, которая берет строку и возвращает число: DG>
DG>int atoi (string s);
DG>
DG>Такая функция должна возвращать exception или не должна?
Имхо если чего конвертим во что то то лучше кидать (то что в шарпе вся конвертация кидается ексепшенами позволяет при отладке такие штуки отловить почти сразу)
DG>Или мне надо сразу писать две функции?: DG>
Если потом придется проверять дефолтный код возврата — чем это лучше чем ловить ексепшен от первого варианта я не понимаю.
DG>И так на каждую библиотечную функцию?
не знаю
Здравствуйте DarkGray, Вы писали:
V>>Ну и нахрена мне в релизе все эти try/catch, если все равно оно не юзается? DG>Кто сказал, что они не юзаются?
Пустой catch(...).
DG>Они говорят о том, что любая ошибка при записи лога (памяти не хватило, диск отвалился) не повлият на работу программы.
А оно и так не повлияет на работу. Тихо вернется игнорируемая ошибка и все.
[...] V>>Ну чем этот вариант хуже??? DG>Тем, что в общем случае в release он будет валит всю программу (например, если file-у или operator-у << не хватило памяти)
Если нехватит памяти это поймается в совершенно другом месте и все будет хорошо (хотя уже вряд-ли А мусора в программе в виде пустых try/catch (либо, еще хуже, врапперов над библиотечными функциями с пустыми try/catch) будет намного меньше.
class X
{
public:
X(){throw 0;}
};
class Y
{
X x;
};
Можно сколько угодно рассуждать о принципиальной невозможности существования Y, если не может существовать агрегируемый X, только на практике это приведет к очередному извращению, типа:
class Y
{
std::auto_ptr<X> x;
public:
Y()
{
try
{
x.reset(new X);
}
catch (...)
{
}
}
};