Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

про языковые конструкции

64K
15 сентября 2011 года
az20110303
29 / / 13.09.2011
Давайте пофлудим на тему, какие конструкции в языках программирования на ваш взгляд наиболее удобы и облегчают работу и в какой ситуации, а в какой, наоборот - усложняют.

Для примера, мне часто нехватает оператора goto в ООП. Пусть есть такая функция (или метод, неважно):

Код:
handleResult initSomething() {
 
  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 initSomething() {
  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 код выглядит извращенно.
274
15 сентября 2011 года
Lone Wolf
1.3K / / 26.11.2006
писать уметь надо. и без goto справишся
240
15 сентября 2011 года
aks
2.5K / / 14.07.2006
Проблема в том что тут во первых нет ООП заявленного в начале. А во вторых код, несмотря на то что является кодом на С++, фактически является кодом на С, где goto действительно принято использовать для подобной обработки ошибок.
В С++ например при выходе за область видимости, где они были созданы, твои объекты handle автоматически удаляться и деструкторы подчистят и закроют нужные хэндлы.
245
15 сентября 2011 года
~ArchimeD~
1.4K / / 24.07.2006
Цитата: az20110303

Для примера, мне часто нехватает оператора goto в ООП.



Еретик!!! Анафемец!!! Сжечь на костре

278
15 сентября 2011 года
Alexander92
1.1K / / 04.08.2008
И причем здесь goto к ООП? Какая связь?
274
15 сентября 2011 года
Lone Wolf
1.3K / / 26.11.2006
Цитата: Alexander92
И причем здесь goto к ООП? Какая связь?


Ни при чем. человек пока неумеет пользоваться концептами ООП

64K
15 сентября 2011 года
az20110303
29 / / 13.09.2011
Цитата: aks

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


handle-это uintы, а процедуры, которые с ними работаю, находятся в динамической библиотеке. а основная процедура - статический метод.

64K
15 сентября 2011 года
az20110303
29 / / 13.09.2011
Цитата: Lone Wolf
Ни при чем. человек пока неумеет пользоваться концептами ООП



Вот что я накодил в общих чертах. Объясните мне, где я не соблюл концепции. (да, ооп - не самая сильная моя сторона.)

Код:
class MyClass {
  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");
}
278
15 сентября 2011 года
Alexander92
1.1K / / 04.08.2008
[QUOTE=az20110303]
Объясните мне, где я не соблюл концепции
[/QUOTE]
Во-первых, в рамках ООП вы не должны писать каждый раз писать closeOne(), closeTwo() и т.д. Эти функции должен брать на себя деструктор. Во-вторых, с какой радости вы делаете инициализацию статической? В-третьих, немного не по теме, но все же, "We failed!!!" - не самая удачная диагностика, мягко говоря. Продолжать?
64K
15 сентября 2011 года
az20110303
29 / / 13.09.2011
Цитата: Alexander92
Во-первых, в рамках ООП вы не должны писать каждый раз писать closeOne(), closeTwo() и т.д. Эти функции должен брать на себя деструктор.




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

Цитата: Alexander92
В-третьих, немного не по теме, но все же, "We failed!!!" - не самая удачная диагностика, мягко говоря.


Это просто пример. Я nda подписал ;)

278
15 сентября 2011 года
Alexander92
1.1K / / 04.08.2008
Ну значит, изначальное проектирование кривое, что я еще могу добавить к этому?
[QUOTE=az20110303]
handle-это uintы
[/QUOTE]
Хотя бы вот это. Если объект имеет смысл uint'а, он должен инкапсулировать соответствующее поле, а простое переобозначение uint'а в handle или что-то подобное - это C-style, но никак не C++ и не ООП.
64K
15 сентября 2011 года
az20110303
29 / / 13.09.2011
Вообще тот код, который я привел - это переделка старого C кода в С++. Видимо, люди, занимавшиеся этим, не сочли нужным обрамлять каждый отдельный хэндл классом.

Цитата: aks
В С++ например при выходе за область видимости, где они были созданы, твои объекты handle автоматически удаляться и деструкторы подчистят и закроют нужные хэндлы.


А за эту инфу спасибо. Действительно, если бы хэндлы были классами, то закрылись бы при выходе из метода.

245
15 сентября 2011 года
~ArchimeD~
1.4K / / 24.07.2006
Цитата: az20110303
если бы хэндлы были классами, то закрылись бы при выходе из метода.



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

7
15 сентября 2011 года
@pixo $oft
3.4K / / 20.09.2006
Охмайгадабл,коденет!:facepalm:
Код:
HANDLE initSomething(){
    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;
}
Конечно,я понимаю,что от моего ассемблерного прошлого осталось много goto,но даже этот пример я смог переписать
274
15 сентября 2011 года
Lone Wolf
1.3K / / 26.11.2006
@pixo $oft привильно говорит. мне лень было этот пример постить. только замечание - h - изначально 0 иницилизируешь и все. и при возврате никаких условий
245
15 сентября 2011 года
~ArchimeD~
1.4K / / 24.07.2006
к слову, не забываем, что ограничение по области видимости в c++ можно создать просто фигурными скобками
Код:
int main (void)
{
    {
        c_handle handle("handle1");
        handle.dosomething();
    }
    // к этому месту handle будет уже разрушен
    {
        c_handle handle("handle2");
    }
    // к этому месту другой handle тоже будет уже разрушен
}


особенно полезно для критических секций кода, залоченных по scoped_lock - по выходу из блока блокировка автоматически снимается с разрушением лочащего объекта
240
15 сентября 2011 года
aks
2.5K / / 14.07.2006
Цитата: az20110303
handle-это uintы, а процедуры, которые с ними работаю, находятся в динамической библиотеке. а основная процедура - статический метод.


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

1.8K
15 сентября 2011 года
LM(AL/M)
332 / / 20.12.2005
Цитата: @pixo $oft
Охмайгадабл,коденет!:facepalm:
Код:
HANDLE initSomething(){
    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;
}
Конечно,я понимаю,что от моего ассемблерного прошлого осталось много goto,но даже этот пример я смог переписать



этот пример еще хуже исходного

вот как пишут настоящие хакеры:

 
Код:
HANDLE h1 = initOne();
HANDLE h2 = h1 ? initTwo() : 0;
HANDLE h3 = h2 ? initThree() : 0;

if (h3) return h3;

if (h2) closeTwo();
if (h1) closeOne();


а вообще в идеале должно быть так:

 
Код:
initOne();
initTwo();
initThree();
...


если есть проблемы -- ловим исключение и чистим все в деструкторе

P.S. у меня кстати тоже ассемблерное прошлое ))
7
16 сентября 2011 года
@pixo $oft
3.4K / / 20.09.2006
Цитата: LM(AL/M)
этот пример еще хуже исходного

Вот только не надо ля-ля:p
А твой вариант…ну да,более продвинут,не спорю ☺

274
17 сентября 2011 года
Lone Wolf
1.3K / / 26.11.2006
Вариант LM(AL/M) не канает при даных условиях вобще-то)) Хотя в идеале вариант более правильный
это так справедливости ради.
1.8K
17 сентября 2011 года
LM(AL/M)
332 / / 20.12.2005
Цитата: @pixo $oft
Вот только не надо ля-ля:p



надо...
потому что за вложенные ифы вообще отстреливать надо

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

7
17 сентября 2011 года
@pixo $oft
3.4K / / 20.09.2006
Цитата: LM(AL/M)
потому что за вложенные ифы вообще отстреливать надо

Ожидаю хорошей аргументации

Цитата: LM(AL/M)
нефиг писать фигню в которую потом надо втупляться

Это точно:)[QUOTE=LM(AL/M)]если есть проблемы -- ловим исключение[/QUOTE]А если там без исключений случилась ошибка?Такой линейный вызов процедур не сказать,что хорошо выглядит

245
17 сентября 2011 года
~ArchimeD~
1.4K / / 24.07.2006
Цитата: @pixo $oft

[QUOTE=LM(AL/M);357513]надо...
потому что за вложенные ифы вообще отстреливать надо


Ожидаю хорошей аргументации[/QUOTE]
я думаю, АК74М является хорошим и весомым аргументом :D

7
17 сентября 2011 года
@pixo $oft
3.4K / / 20.09.2006
Цитата: ~ArchimeD~
я думаю, АК74М является хорошим и весомым аргументом :D

Может,и является,но думается мне,что я даже его не дождусь

1.8K
19 сентября 2011 года
LM(AL/M)
332 / / 20.12.2005
Цитата: @pixo $oft
Ожидаю хорошей аргументации


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

Цитата: @pixo $oft
А если там без исключений случилась ошибка?


если это наш код (а здесь как я понял именно такой случай), то в нашей власти сделать так чтобы обработка ошибок осуществлялась только посредством механизма исключений

7
19 сентября 2011 года
@pixo $oft
3.4K / / 20.09.2006
Цитата: LM(AL/M)
вложенные if-ы -- просто уродливая конструкция, слишком уродливая чтобы с этим мириться...

Ну чистый субъективизм же.Право,не знаю,что в ней такого уродливого

Цитата: LM(AL/M)
в нашей власти сделать так чтобы обработка ошибок осуществлялась только посредством механизма исключений

Ага,т.е. писать код,который вызовет другой код?Да ещё и из другого места?Громоздко,в то время как if'ы справляются с этим на ура.В случае с исключениями–фактически отложенный goto,ибо выполнение перескакивает в другое место программы(возможно,даже в другую процедуру)

278
19 сентября 2011 года
Alexander92
1.1K / / 04.08.2008
[QUOTE=LM(AL/M)]
в нашей власти сделать так чтобы обработка ошибок осуществлялась только посредством механизма исключений
[/QUOTE]
Мне кажется, перевешивать обработку абсолютно всех ошибок на исключения тоже не есть красивое решение. Не помню точно, где читал этот пример, - по-моему, у Шилдта; суть в том, что автор объясняет механизм исключений на примере контроля ввода пользователем телефонного номера или чего-то в этом духе. В то же время, несколькими строками ниже идет ремарка примерно следующего содержания: "Разумеется, в реальной жизни пользователи частенько вводят данные неправильно, поэтому такая ситуация отнюдь не является исключительной; соответственно, в реальном приложении я бы обработал такую ошибку с помощью обычного блока if".
87
19 сентября 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: LM(AL/M)
а вообще в идеале должно быть так:

 
Код:
initOne();
initTwo();
initThree();
...


если есть проблемы -- ловим исключение и чистим все в деструкторе



тогда уж:

 
Код:
handle init_all()
{
    return init_three(init_two(init_one()));
}


но такой подход не очень хорош для C++. Более приемлемо для языков со сборкой мусора, с развитой системой исключений и т.д. Такое моё скромное мнение.
64K
20 сентября 2011 года
az20110303
29 / / 13.09.2011
Как вам такой код, соответствует фэншуйским ООПшным концепциям, не?

Код:
class handleOneClass {
  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().
Чтоб точно по фэншую надо тогда так что-ли

Код:
class handleOneClass {
  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() как ему хочется.
278
20 сентября 2011 года
Alexander92
1.1K / / 04.08.2008
Ну дык другое дело ведь! Единственное замечание - по фен-шую лучше писать
 
Код:
handleOne& handle() const {
  return m_handle;
}


P.S. Посмотрел повнимательнее. Ежели handleOne у вас тоже класс, то вы не должны писать вручную closeOne(), это тоже должен брать на себя деструктор. Либо, по крайней мере, это должно быть реализовано через функцию-член:
 
Код:
m_handle->Close();

А если это у вас НЕ класс, то это не по фен-шую, опять же. :)
240
20 сентября 2011 года
aks
2.5K / / 14.07.2006
Цитата: @pixo $oft
Ага,т.е. писать код,который вызовет другой код? Да ещё и из другого места? Громоздко,в то время как if'ы справляются с этим на ура. В случае с исключениями–фактически отложенный goto,ибо выполнение перескакивает в другое место программы(возможно,даже в другую процедуру)


Исключения не вызывают другой код из другого места. =) Или return по твоему тоже вызывает другой код из другого места?
Главное отличие от return - что он возвращается к предыдущему уровню стека только. А исключения раскручивает стек до того уровня, где уже знают что с ним делать. А это место далеко не всегда на предыдущем уровне стека. В обоих случаях ты попадаешь в место с вполне конкретными состояниями, оставленными перед вызовом.
Ну и плюс исключения могут быть разных типов, в отличие от ретурна, что тоже является одним из признаков описания ошибки.

41K
22 сентября 2011 года
kisssko
108 / / 28.10.2010
Цитата: az20110303
handle-это uintы, а процедуры, которые с ними работаю, находятся в динамической библиотеке. а основная процедура - статический метод.



Если быть более точным, HANDLE - это синоним типа void*. Указатель (адрес) на неопределённый тип данных.

278
22 сентября 2011 года
Alexander92
1.1K / / 04.08.2008
Цитата: kisssko
Если быть более точным, HANDLE - это синоним типа void*. Указатель (адрес) на неопределённый тип данных.


Там handle - это не HANDLE из WinAPI, это какой-то пользовательский тип.

41K
22 сентября 2011 года
kisssko
108 / / 28.10.2010
Как альтернатива goto вполне сгодится блок while(TRUE){...}. Оператор break в блоке - как раз аналог goto к коду за закрывающей скобкой. И ещё оператор switch полезен бывает.

Так что можно написать так:

Код:
HANDLE initSomething(){
   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;
}
240
23 сентября 2011 года
aks
2.5K / / 14.07.2006
Повторюсь, что автор goto использовал не для выхода из цикла, а для обработки ошибок в одном месте. Потому что пишет на C, а там такой подход применяют.
Ошибка может возникнуть и внутри циклов и свичей и ни везде есть возможность break-ом попасть в обработчик ошибок.
41K
23 сентября 2011 года
kisssko
108 / / 28.10.2010
Цитата: aks
Повторюсь, что автор goto использовал не для выхода из цикла, а для обработки ошибок в одном месте. Потому что пишет на C, а там такой подход применяют.
Ошибка может возникнуть и внутри циклов и свичей и ни везде есть возможность break-ом попасть в обработчик ошибок.



Применяют, но в любом случае можно и без него обойтись.
А while - это не всегда цикл, а и просто блок кода для однократного выполнения.
И в моём примере внутри switch подразумевается код, где ошибки быть не должно.
Иначе был бы уже другой пример. :)

240
23 сентября 2011 года
aks
2.5K / / 14.07.2006
Цитата: kisssko
Применяют, но в любом случае можно и без него обойтись.


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

Цитата: kisssko

А while - это не всегда цикл, а и просто блок кода для однократного выполнения.


{...} - вот это блок кода, а while по определению цикл. Еще раз обращаю внимание - ошибка может произойти в любом месте кода, и вовсе необязательно что это место не будет внутри циклов, свитчей, одной из многих веток условных операторов и прочего. Поэтому break для обработки ошибок на манер goto в C не годиться в общем случае. В C++ уже таких проблем например нет.

87
23 сентября 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: aks
В C++ уже таких проблем например нет.



Всё-таки в C++ проблемы с использованием исключений есть. Например:
http://www.rsdn.ru/forum/cpp/1519764.flat.aspx
http://stackoverflow.com/questions/3180268/why-are-c-stl-iostreams-not-exception-friendly

В общем, есть "подводные камни".

341
23 сентября 2011 года
Der Meister
874 / / 21.12.2007
Цитата: Kogrom
Всё-таки в C++ проблемы с использованием исключений есть. Например: ...

Эмм... Разве это проблемы? В одном случае, каждый раз при делении класть обработчик объективно жирно, во втором нужно выброс исключения "включить".

87
23 сентября 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: Der Meister
Эмм... Разве это проблемы?



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

Цитата: Der Meister
В одном случае, каждый раз при делении класть обработчик объективно жирно



Ничего не жирно. Майкрософт и Борланд прикрутили костыли - никто не жалуется.

Цитата: Der Meister
во втором нужно выброс исключения "включить"



Вот тут проявляется сущность C++ : "скорость имеет больший приоритет, чем надёжность". Я бы сделал, чтобы по умолчанию было включено. То же и с std::vector.at().

С другой стороны, в new включено в основной версии, отключено в перегруженной. Непоследовательно.

41K
23 сентября 2011 года
kisssko
108 / / 28.10.2010
Цитата: aks
Разве кто то спорит? Речь о том, что в С это стандартный подход и он там вполне оправдан - без него получается много копипаста и нагромождения кода.


Мой пример получился ни чуть не более громоздкий, чем вариант от ТС с goto.
Да, goto бывает иногда оправдан, но только не в этом случае, а в более узкоспециализированных.
Например - эмулятор ЦПУ, либо интерпретатор байткода. И то, выигрыш в использовании goto
сводится лишь к повышению производительности, по сравнению с использованием вызовов.

Цитата: aks
{...} - вот это блок кода, а while по определению цикл.


Ну хорошо, цикл... :) Только выполняемый не более одного раза.

Цитата: aks
Еще раз обращаю внимание - ошибка может произойти в любом месте кода, и вовсе необязательно что это место не будет внутри циклов, свитчей, одной из многих веток условных операторов и прочего.


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

Цитата: aks
Поэтому break для обработки ошибок на манер goto в C не годиться в общем случае. В C++ уже таких проблем например нет.


Странно. Почему то сгодился. :) И даже если где то использование goto для обработки ошибок действительно оправданно,
то данный случай к таким не относится.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог