про языковые конструкции
Для примера, мне часто нехватает оператора goto в ООП. Пусть есть такая функция (или метод, неважно):
handleOne h1 = initOne();
if ( !h1 ) {
return 0;
}
handleTwo h2 = initTwo(h1);
if ( !h2 ) {
closeOne(h1);
return 0;
}
handleThree h3 = initThree(h2);
if ( !h3 ) {
closeTwo(h2);
closeOne(h1);
return 0;
}
handleResult h = initResult(h3);
//after h is acquired, h1,h2,h3 can be disposed.
closeThree(h3);
closeTwo(h2);
closeOne(h1);
return h;
}
C goto всё становится гораздо проще:
handleResult h = 0;
handleOne h1 = initOne();
if ( !h1 ) goto exit;
handleTwo h2 = initTwo(h1);
if ( !h2 ) goto close1;
handleThree h3 = initThree(h2);
if ( !h3 ) goto close2;
handleResult h = initResult(h3);
//after h is acquired, h1,h2,h3 can be disposed.
closeThree(h3);
close2:
closeTwo(h2);
close1:
closeOne(h1);
exit:
return h;
}
Без goto код выглядит извращенно.
В С++ например при выходе за область видимости, где они были созданы, твои объекты handle автоматически удаляться и деструкторы подчистят и закроют нужные хэндлы.
Для примера, мне часто нехватает оператора goto в ООП.
Еретик!!! Анафемец!!! Сжечь на костре
Ни при чем. человек пока неумеет пользоваться концептами ООП
В С++ например при выходе за область видимости, где они были созданы, твои объекты handle автоматически удаляться и деструкторы подчстят и закроют нужные хэндлы.
handle-это uintы, а процедуры, которые с ними работаю, находятся в динамической библиотеке. а основная процедура - статический метод.
Вот что я накодил в общих чертах. Объясните мне, где я не соблюл концепции. (да, ооп - не самая сильная моя сторона.)
private:
handleResult myHandle;
protected:
static handleResult initSomething();
public:
MyClass();
};
handleResult MyClass::initSomething() {
//some code here
handleOne h1 = initOne();
if ( !h1 ) {
return 0;
}
handleTwo h2 = initTwo(h1);
if ( !h2 ) {
closeOne(h1);
return 0;
}
handleThree h3 = initThree(h2);
if ( !h3 ) {
closeTwo(h2);
closeOne(h1);
return 0;
}
handleResult h = initResult(h3);
//after h is acquired, h1,h2,h3 can be disposed.
closeThree(h3);
closeTwo(h2);
closeOne(h1);
return h;
}
MyClass::MyClass() {
//some code here
myHandle = initSomething();
if (!myHandle) throw Exception("We failed!!1");
}
Объясните мне, где я не соблюл концепции
[/QUOTE]
Во-первых, в рамках ООП вы не должны писать каждый раз писать closeOne(), closeTwo() и т.д. Эти функции должен брать на себя деструктор. Во-вторых, с какой радости вы делаете инициализацию статической? В-третьих, немного не по теме, но все же, "We failed!!!" - не самая удачная диагностика, мягко говоря. Продолжать?
Так было сделано до меня. Статический метод вызывается не только в конструкторе, и фактически он только возвращает значение, и никак не изменяет экземпляры класса.
Это просто пример. Я nda подписал ;)
[QUOTE=az20110303]
handle-это uintы
[/QUOTE]
Хотя бы вот это. Если объект имеет смысл uint'а, он должен инкапсулировать соответствующее поле, а простое переобозначение uint'а в handle или что-то подобное - это C-style, но никак не C++ и не ООП.
А за эту инфу спасибо. Действительно, если бы хэндлы были классами, то закрылись бы при выходе из метода.
вот и ответ собственно говоря :) в сферическом ооп в вакууме, например в java, все должно быть классом. а тут винигред судя по всему. по крайней мере то, что явным образом напрашивается отдельной сущностью - в класс не вынесено.
HANDLE h1=initOne();
if(h1){
HANDLE h2=initTwo(h1);
if(h2){
HANDLE h3=initThree(h2);
if(h3){
h=initResult(h3);
closeThree(h3);
}
closeTwo(h2);
}
closeOne(h1);
}
if(h)return h; else return 0;
//или,в зависимости от условий,
//if(h3)return h; else return 0;
}
{
{
c_handle handle("handle1");
handle.dosomething();
}
// к этому месту handle будет уже разрушен
{
c_handle handle("handle2");
}
// к этому месту другой handle тоже будет уже разрушен
}
особенно полезно для критических секций кода, залоченных по scoped_lock - по выходу из блока блокировка автоматически снимается с разрушением лочащего объекта
Вот я и говорю -код написан фактически на С. В С применяют goto для обработки ошибок за неимением лучшего.
К примеру делаешь класс представляющий собой сущность хэндла. Не каждый хэндл, а именно выносишь туда общую часть для всех. Если надо огранизуешь иерархию хэндлов. И проблема решена без goto.
HANDLE h1=initOne();
if(h1){
HANDLE h2=initTwo(h1);
if(h2){
HANDLE h3=initThree(h2);
if(h3){
h=initResult(h3);
closeThree(h3);
}
closeTwo(h2);
}
closeOne(h1);
}
if(h)return h; else return 0;
//или,в зависимости от условий,
//if(h3)return h; else return 0;
}
этот пример еще хуже исходного
вот как пишут настоящие хакеры:
HANDLE h2 = h1 ? initTwo() : 0;
HANDLE h3 = h2 ? initThree() : 0;
if (h3) return h3;
if (h2) closeTwo();
if (h1) closeOne();
а вообще в идеале должно быть так:
initTwo();
initThree();
...
если есть проблемы -- ловим исключение и чистим все в деструкторе
P.S. у меня кстати тоже ассемблерное прошлое ))
Вот только не надо ля-ля:p
А твой вариант…ну да,более продвинут,не спорю ☺
это так справедливости ради.
надо...
потому что за вложенные ифы вообще отстреливать надо
+ канает или нет мой вариант при данных условиях тоже не важно потому что нехрен создавать такие условия... иными словами нефиг писать фигню в которую потом надо втупляться
Ожидаю хорошей аргументации
Это точно:)[QUOTE=LM(AL/M)]если есть проблемы -- ловим исключение[/QUOTE]А если там без исключений случилась ошибка?Такой линейный вызов процедур не сказать,что хорошо выглядит
[QUOTE=LM(AL/M);357513]надо...
потому что за вложенные ифы вообще отстреливать надо
Ожидаю хорошей аргументации[/QUOTE]
я думаю, АК74М является хорошим и весомым аргументом :D
Может,и является,но думается мне,что я даже его не дождусь
всё просто: вложенные if-ы -- просто уродливая конструкция, слишком уродливая чтобы с этим мириться...
для меня это очень хорошая аргументация
если это наш код (а здесь как я понял именно такой случай), то в нашей власти сделать так чтобы обработка ошибок осуществлялась только посредством механизма исключений
Ну чистый субъективизм же.Право,не знаю,что в ней такого уродливого
Ага,т.е. писать код,который вызовет другой код?Да ещё и из другого места?Громоздко,в то время как if'ы справляются с этим на ура.В случае с исключениями–фактически отложенный goto,ибо выполнение перескакивает в другое место программы(возможно,даже в другую процедуру)
в нашей власти сделать так чтобы обработка ошибок осуществлялась только посредством механизма исключений
[/QUOTE]
Мне кажется, перевешивать обработку абсолютно всех ошибок на исключения тоже не есть красивое решение. Не помню точно, где читал этот пример, - по-моему, у Шилдта; суть в том, что автор объясняет механизм исключений на примере контроля ввода пользователем телефонного номера или чего-то в этом духе. В то же время, несколькими строками ниже идет ремарка примерно следующего содержания: "Разумеется, в реальной жизни пользователи частенько вводят данные неправильно, поэтому такая ситуация отнюдь не является исключительной; соответственно, в реальном приложении я бы обработал такую ошибку с помощью обычного блока if".
initTwo();
initThree();
...
если есть проблемы -- ловим исключение и чистим все в деструкторе
тогда уж:
{
return init_three(init_two(init_one()));
}
но такой подход не очень хорош для C++. Более приемлемо для языков со сборкой мусора, с развитой системой исключений и т.д. Такое моё скромное мнение.
private:
handleOne m_handle;
public:
handleOneClass() {
m_handle = initOne();
if (!m_handle) throw new Exception("Failed to initialize first class");
}
handleOne handle() {
return m_handle;
}
~handleOneClass() {
if (m_handle)
closeOne(m_handle);
}
};
В общем, для себя решил, что от польза свистелок в виде ООП - сомнительная.
UPD: Похоже не совсем соответствует, так как "внутреннее поле" выглядывает наружу - метод handleOne handle().
Чтоб точно по фэншую надо тогда так что-ли
friend class handleTwoClass;
private:
handleOne m_handle;
protected:
handleOne handle() {
return m_handle;
}
public:
handleOneClass() {
m_handle = initOne();
if (!m_handle) throw new Exception("Failed to initialize first class");
}
~handleOneClass() {
if (m_handle)
closeOne(m_handle);
}
};
А класс handleTwoClass уже использует метод handleOneClass::handle() как ему хочется.
return m_handle;
}
P.S. Посмотрел повнимательнее. Ежели handleOne у вас тоже класс, то вы не должны писать вручную closeOne(), это тоже должен брать на себя деструктор. Либо, по крайней мере, это должно быть реализовано через функцию-член:
А если это у вас НЕ класс, то это не по фен-шую, опять же. :)
Исключения не вызывают другой код из другого места. =) Или return по твоему тоже вызывает другой код из другого места?
Главное отличие от return - что он возвращается к предыдущему уровню стека только. А исключения раскручивает стек до того уровня, где уже знают что с ним делать. А это место далеко не всегда на предыдущем уровне стека. В обоих случаях ты попадаешь в место с вполне конкретными состояниями, оставленными перед вызовом.
Ну и плюс исключения могут быть разных типов, в отличие от ретурна, что тоже является одним из признаков описания ошибки.
Если быть более точным, HANDLE - это синоним типа void*. Указатель (адрес) на неопределённый тип данных.
Там handle - это не HANDLE из WinAPI, это какой-то пользовательский тип.
Так что можно написать так:
int stage=0;
HANDLE h=0;
while(1){
HANDLE h1=initOne(); if(h1){stage++;}else{break;}
HANDLE h2=initTwo(h1); if(h2){stage++;}else{break;}
HANDLE h3=initThree(h2);if(h3){stage++;}else{break;}
h=initResult(h3);
break;
}
switch(stage){
case 3: closeThree(h3);
case 2: closeTwo(h2);
case 1: closeOne(h1);
default: ;
}
return h;
}
Ошибка может возникнуть и внутри циклов и свичей и ни везде есть возможность break-ом попасть в обработчик ошибок.
Ошибка может возникнуть и внутри циклов и свичей и ни везде есть возможность break-ом попасть в обработчик ошибок.
Применяют, но в любом случае можно и без него обойтись.
А while - это не всегда цикл, а и просто блок кода для однократного выполнения.
И в моём примере внутри switch подразумевается код, где ошибки быть не должно.
Иначе был бы уже другой пример. :)
Разве кто то спорит? Речь о том, что в С это стандартный подход и он там вполне оправдан - без него получается много копипаста и нагромождения кода.
А while - это не всегда цикл, а и просто блок кода для однократного выполнения.
{...} - вот это блок кода, а while по определению цикл. Еще раз обращаю внимание - ошибка может произойти в любом месте кода, и вовсе необязательно что это место не будет внутри циклов, свитчей, одной из многих веток условных операторов и прочего. Поэтому break для обработки ошибок на манер goto в C не годиться в общем случае. В C++ уже таких проблем например нет.
Всё-таки в C++ проблемы с использованием исключений есть. Например:
http://www.rsdn.ru/forum/cpp/1519764.flat.aspx
http://stackoverflow.com/questions/3180268/why-are-c-stl-iostreams-not-exception-friendly
В общем, есть "подводные камни".
Эмм... Разве это проблемы? В одном случае, каждый раз при делении класть обработчик объективно жирно, во втором нужно выброс исключения "включить".
Проблемы в неинтуитивности, в основном. В каждом случае надо подвох искать. Чуть зазевался - в программе баг.
Ничего не жирно. Майкрософт и Борланд прикрутили костыли - никто не жалуется.
Вот тут проявляется сущность C++ : "скорость имеет больший приоритет, чем надёжность". Я бы сделал, чтобы по умолчанию было включено. То же и с std::vector.at().
С другой стороны, в new включено в основной версии, отключено в перегруженной. Непоследовательно.
Мой пример получился ни чуть не более громоздкий, чем вариант от ТС с goto.
Да, goto бывает иногда оправдан, но только не в этом случае, а в более узкоспециализированных.
Например - эмулятор ЦПУ, либо интерпретатор байткода. И то, выигрыш в использовании goto
сводится лишь к повышению производительности, по сравнению с использованием вызовов.
Ну хорошо, цикл... :) Только выполняемый не более одного раза.
Может произойти... Из за ошибок в коде, или аппаратного глюка. От этого уже не застрахуешься. Изначально же речь шла об ожидаемых ошибках.
Например, когда функция API информирует об ошибке.
Странно. Почему то сгодился. :) И даже если где то использование goto для обработки ошибок действительно оправданно,
то данный случай к таким не относится.