Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Завершение функции return'ом - хороший ли тон? :), есть ли какие-то за или против этого? 
:(
    Опции темы
Proger10
Дата 9.6.2013, 04:44 (ссылка) |   (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 312
Регистрация: 16.12.2008

Репутация: нет
Всего: нет



Задумался о таком вопросе. Прерывать функцию можно как-то так:
Код

bool isItem() {
...
  if ( found ) {
    return true;
  }
...
  if ( found2 ) {
    return false;
  }
...
  return false;
}

Ну что-то в таком духе. Мне интересно - насколько часто такое используется и насколько это хороший тон программирования? Загуглил, но ничего внятного на эту тему не получил. Почему спрашиваю - во времена Бейсика были такие операторы, как GoTo (goto) или путаю с Паскалем...) ну не суть важно, в Паскале метки были и выборочно тоже из кода можно было перепрыгнуть на какую-либо метку в любую точку программы. Это считалось очень нехорошим тоном, поскольку могло сильно запутывать программиста, да и сильно снижать читабельность кода в целом.

Хочу спросить относительно вот таких return'ов. Какое у Вас отношение к такому коду?
В данном случае:
Код

bool isItem(){
  bool result = false;
.......
  return result;
}

прошлый подход помогает избежать создания дополнительной переменной и соответственно выделения под неё памяти, пускай да и немного smile Но всё-таки, что думаете об этих подходах?
PM MAIL   Вверх
Dem_max
Дата 9.6.2013, 05:15 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 4
Всего: 39



Не парься Microsoft не гнушается использовать  goto в функциях, так в некоторых моментах это самый наилучший и самый простой вариант.


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
Arantir
Дата 9.6.2013, 05:27 (ссылка) |   (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: нет
Всего: 55



Если принять во внимание, что функция по идее своей не должна содержать столько уж дофига кода, чтобы return посередине делал ее намного быстрее (даже не смотря на то, что в оставшейся части кода ничего полезного не сделается), то return-ать через каждые пару строчек не очень хорошо.
Это действительно может сбить с толку тем, что выполнение до некоторых строчек вообще не дойдет при некоторых обстоятельствах.

На самом деле удобство кода — довольно важная часть разработки в наше время. Проекты не делаются на один раз, они еще долго поддерживаются и развиваются. Так что удобный код нужен для этого развития. Если подумать, то и ООП заставляет компьютер делать много лишней работы, но его удобство все же перевешивает.

Пару лишних if-ов погоды не сделают, разве что только пару тысяч раз в секунду эту функцию вызывать.

Впрочем, в 10-20-и строчках return-ы  не заметить довольно трудно. Так что это дело вкуса. return тоже о чем-то говорит, например о том, что тут функция обязана прекратится в любом случае. Это может быть таким себе семантическим намеком.

Это сообщение отредактировал(а) Arantir - 9.6.2013, 05:31


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
maxim1000
Дата 9.6.2013, 07:35 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



В C действительно лучше, чтобы был один return. Дело в том, что как бы ни завершалась функция, нужно освобождать ресурсы (файлы закрывать, память освобождать, и т.п.). Писать это возле каждого return'а напряжно и вероятность забыть высокая.

В C++ к освобождению ресурсов другой подход (этим занимаются деструкторы), поэтому иметь несколько return'ов вполне нормально. Если в каком-то месте уже понятно, что вернуть и больше ничего не нужно делать, лучше написать return. Если писать что-то goto-образное или добавлять флажки и if'ы, только читабельность уменьшиться.

Пример:

Код

double sqrt(double value)
{
    if(value<0)
        return not_a_number;
    //сам алгоритм вычисления корня
    ...
    return root
}


Здесь код показывает, что проверка на отрицательность к алгоритму отношения почти не имеет - просто проверка допустимых значений. Был бы это большой if (большой, потому что в самом алгоритме парой строк уже не обойтись), проследить логику было бы сложнее.

Добавлено через 4 минуты и 19 секунд
да, кстати, по поводу скорости и памяти:
1. скорость отличаться будет в пределах погрешности
2. память - очень сильно рекомендую забыть об экономии памяти на стеке, если речь идёт об отдельных значениях типа bool - она всё равно освободиться по выходу, так что остальной программе ни холодно, ни жарко от этой "экономии", конечно больших вещей туда пихать не нужно (массив на миллион лучше всё-таки в кучу), но про маленькие не стоит париться


--------------------
qqq
PM WWW   Вверх
Alexeis
Дата 9.6.2013, 10:43 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 4
Всего: 459



  Я у себя практикую такую манеру (где есть необходимость в сильном ветвлении). Оборачиваю всю логику в try / catch . Правильный ход событий делается линейным без ветвлений и он тянется до конца try блока, в случае чего-то нештатного, я бросаю исключение типа const wchar_t* с текстом ошибки. В catch блоке делаются действия специфичные для любых ошибок и для этой в частности (если нужно что-то специально делать). Результат сохраняется в временной переменной и после после catch блока делаются операции, которые нужно произвести как для состояния ошибки так и для успеха, после чего возвращается результат работы. Получается одна точка выхода. Если ничего освобождать не нужно специально, то делаются 2е точки выхода - одна для успеха, другая для ошибки.

Этот ответ добавлен с нового Винграда - http://vingrad.com
PM ICQ Skype   Вверх
NoviceF
Дата 9.6.2013, 12:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: 2
Всего: 2



Цитата(Proger10 @  9.6.2013,  05:44 Найти цитируемый пост)
Хочу спросить относительно вот таких return'ов.

Тут 2 немного разные темы затронуты - false в середине функции - это сигнализирование об ошибке, вполне нормальное поведение. В зависимости от серзьёности ошибки можно рассмотреть возможность вместо возврата false кинуть исключение. А вот ветвление и возврат true, это другая ситуация. Не знаю, может это какой-то стереотип, но на мой взгляд такой подход более естественнен для возврата булевых значений:

Код

bool isItem(const Object& found) {
...
  if (found.isSpirit() || found.isSpace())  // не соответствует изначальному предположению
  {
    return false;
  }

...                                                           // другие проверки

  return true; // если ошибок в процессе выполнения функции не возникло - изначальное предположение истинно.
}


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

Код

std::string FindSubstr(const std::string& source, const char* substr)
{
    if (source.find(substr) != std::string::npos)
        return source.substr(source.find(substr), strlen(substr));

    ...                              // другие возможные проверки и возврат инициализированной строки

    return std::string();  // при неудаче возвращаем пустую строку
}


Пример, естественно, надуманный, но принцип поясняет.

В общем, зависит от контекста smile

Ну и за утекающими ресурсами нужно следить - использовать для них управляющие объекты.

PM MAIL   Вверх
Dem_max
Дата 9.6.2013, 15:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 4
Всего: 39



Цитата

Дело в том, что как бы ни завершалась функция, нужно освобождать ресурсы (файлы закрывать, память освобождать, и т.п.). 

Угу для этого GOTO в определенное место, а там уже освободить ресурсы и сделать RETURN


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
Alexeis
Дата 10.6.2013, 10:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 4
Всего: 459



Цитата(NoviceF @  9.6.2013,  13:54 Найти цитируемый пост)
А вот ветвление и возврат true, это другая ситуация. Не знаю, может это какой-то стереотип, но на мой взгляд такой подход более естественнен для возврата булевых значений:

  Речь именно о ветвлении с целью проверки на состоянии ошибки. Например для избежания такого ветвления.
Код

  if (dosmf1(a))
  {
        c = dosmf2(b);
        if (c > N)
        {
            e = dosmf2(d);
            if (e && a)
            {
                return dosmf2(e + a);
            }
            else
                return false;
    }
        else
            return false;
  }
  else
      return false;

   По моему, исключения шикарный механизм заменяющий goto в ситуациях когда нужно отработать неуспех функции. 
 
Цитата(NoviceF @  9.6.2013,  13:54 Найти цитируемый пост)
Ну и за утекающими ресурсами нужно следить - использовать для них управляющие объекты.

  Ресурсы это не всегда память. Будешь для каждого типа писать управляющие объекты? Кроме того, на освобождении бывает замешана синхронизация потоков. 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
maxim1000
Дата 10.6.2013, 10:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



Цитата(Alexeis @  10.6.2013,  10:00 Найти цитируемый пост)
Ресурсы это не всегда память. Будешь для каждого типа писать управляющие объекты?

да, и это нормально
для памяти и файлов уже есть
для остальных написать несложно, если совсем лениво - можно написать объект вроде on_exit, которому в конструкторе передаётся функтор, который будет вызван при выходе из области видимости

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

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


--------------------
qqq
PM WWW   Вверх
NoviceF
Дата 10.6.2013, 11:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: 2
Всего: 2



Цитата(Alexeis @  10.6.2013,  11:00 Найти цитируемый пост)
 По моему, исключения шикарный механизм заменяющий goto в ситуациях когда нужно отработать неуспех функции. 

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

Цитата(Alexeis @  10.6.2013,  11:00 Найти цитируемый пост)
 Будешь для каждого типа писать управляющие объекты? 

Писать (а лучше использовать готовые) управляющие объекты это C++ путь, использовать ручное управление ресурсами - C путь.
Вообще я бы посмотрел примеры, где использовать управляющие объекты это плохо, и лучше (надёжнее) обойтись без них.
PM MAIL   Вверх
bsa
Дата 10.6.2013, 11:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



Цитата(Proger10 @  9.6.2013,  05:44 Найти цитируемый пост)
Мне интересно - насколько часто такое используется и насколько это хороший тон программирования?
В данном коде нет ничего зазорного, особенно, если он на С++. Очень часто результат работы функции известен до выполнения всего тела (обычно, это отрицательный результат или ошибка). Если такое место только одно, то можно остальной код и в if засунуть. А вот их много? Тогда уровень вложенности блоков станет огромным, и код читать будет крайне неудобно...
С точки зрения производительности проблем тоже нет. Так как компилятор банально ставит операцию перехода на конец функции (бывают исключения), таким образом она завершается штатно.

Если посмотреть исходники драйверов ядра Linux, написанные на С, то там будет полно операторов goto. Потому что ресурсы в ядре необходимо очень четко контролировать, а сделать это через вложенные условные блоки не всегда получается.
PM   Вверх
math64
Дата 10.6.2013, 12:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 12
Всего: 72



Операторы return, goto, continue, break (кроме switch) - мешают рефакторингу. В некоторых IDE можно выделить кусок кода и выделить его в функцию.
А так в них ничего плохого нет - даже полученные ресурсы будут освобождены автоматически (если они освобождаются в деструкторах локальных переменных)
PM   Вверх
Alexeis
Дата 10.6.2013, 16:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 4
Всего: 459



Цитата(maxim1000 @  10.6.2013,  11:14 Найти цитируемый пост)
для остальных написать несложно, если совсем лениво - можно написать объект вроде on_exit, которому в конструкторе передаётся функтор, который будет вызван при выходе из области видимости

  Объект имеет смысл писать если ситуация повторяется. И кроме того, говорю ж, частенько последовательность уничтожения имеет значение. В C# для таких вещей даже придумали финализаторы. Память памятью, а ресурс ресурсом. Или ты объекты синхронизации тоже будешь сигнализировать деструкторами? Такая же история с асинхронной работой. Например с прерываниями. Одна операция может быть разделена на цепочку вызовов. Фактически ресурс захваченный в первом прерывании может быть освобожден в 3м. Т.е. не должен уничтожаться при выходе за границу блока. 
  
Цитата(NoviceF @  10.6.2013,  12:39 Найти цитируемый пост)
По-моему, исключения довольно тяжеловесны и их стоит использовать именно для "Исключительных ситуаций".

  Ну если в системе 64кб памяти, то исключения лучше не использовать, как и многие другие механизмы С++ . При наличии хотя бы 1Мб, это позволительно. 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
maxim1000
Дата 10.6.2013, 18:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



Цитата(Alexeis @  10.6.2013,  16:59 Найти цитируемый пост)
И кроме того, говорю ж, частенько последовательность уничтожения имеет значение.

Как правило "последний захвачен - первый освобождён" покрывает процентов 90 случаев. Для всех остальных вариантов можно использовать динамическое время жизни (например, unique_ptr с его reset). Главное, чтобы в каждом месте программы было очевидно, кто какой объект удаляет. Эту ответственность можно передавать (всё тем же unique_ptr), но если использовать голые указатели, то рано или поздно начнутся утечки.

Касательно объектов синхронизации - только в конструкторах и деструкторах. Если логика их выделения более сложная, будут дедлоки, ибо люди несовершенны. А одна из популярных техник по избежанию дедлоков - иерархический захват мьютексов, который, по сути, и моделируется "последним захвачен - первым освобождён".

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

Это сообщение отредактировал(а) maxim1000 - 10.6.2013, 18:17


--------------------
qqq
PM WWW   Вверх
Alexeis
Дата 10.6.2013, 20:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 4
Всего: 459



Цитата(maxim1000 @  10.6.2013,  19:15 Найти цитируемый пост)
Как правило "последний захвачен - первый освобождён" покрывает процентов 90 случаев. Для всех остальных вариантов можно использовать динамическое время жизни (например, unique_ptr с его reset). 

  Т.е. ты предлагаешь порядок уничтожения задавать при помощи строго заданного порядка определения объектов-переменных в блоке?
  
Цитата(maxim1000 @  10.6.2013,  19:15 Найти цитируемый пост)
Про финализаторы уверенно говорить не могу, но если не ошибаюсь, порядок их вызова недетерминирован, более того, нельзя рассчитывать, что он будет вызван (по крайней мере, в обозримом будущем). Но тут могу ошибаться.

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

Цитата(maxim1000 @  10.6.2013,  19:15 Найти цитируемый пост)
А одна из популярных техник по избежанию дедлоков - иерархический захват мьютексов, который, по сути, и моделируется "последним захвачен - первым освобождён".

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



--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

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

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1409 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.