Bool flag c что это
Перейти к содержимому

Bool flag c что это

  • автор:

Bool flag c что это

Hi,please tell me whats the purpose of bool flag given in following code as I am not able to understand being new to C++.

void display_sp(int n)
<
student st;
ifstream inFile;
inFile.open(«student.dat»,ios::binary);
if(!inFile)
<
cout<<«File could not be open !! Press any Key. «;
cin.ignore();
cin.get();
return;
>
bool flag = false;
while(inFile.read(reinterpret_cast<char *> (&st), sizeof(student)))
<
if(st.retrollno() == n)
<
st.showdata();
flag=true;
>
>
inFile.close();
if(flag == false)
cout<<«\n\nrecord not exist»;
cin.ignore();
cin.get();
>

Looks like the code is reading data from the data file and storing into the student class (by means of casting the data as a string first).

It looks like it’s checking the roll number of the student to see if it matches the argument passed into the function. If it does, it prints the student data. If it doesn’t, it prints that the required student doesn’t exist.

In this case, the only reason the flag is being used is to print to error message at the end. You could bin it completely by simply closing the file stream and returning right after printing the student data.

Флаги в аргументах функций

Эта функция, судя по названию, что-то обрабатывает (process). Но что означают параметры? Какой параметр здесь true, а какой false? По вызывающему коду об этом нельзя судить.

Нам придется заглянуть в объявление функции, которое дает подсказку:

Очевидно, автор использует два параметра типа bool как флаги (toggles). Реализация функции может быть похожа на это:

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

Но я перепутал порядок аргументов.

Столкнувшись с этим багом, программист, вероятно, добавит комментарии к вызову функции, чтобы явно показать свои намерения:

И это слишком похоже на именованные параметры функции — возможность, отсутствующую в C++. Если б она была, то могла бы выглядеть как-нибудь так:

Но если бы даже в C++ такое было, вряд ли это было бы совместимо с прямой передачей (perfect forwarding):

С этим может быть связан еще более коварный баг, который гораздо труднее отследить. Представьте, что функция process — это виртуальный метод класса. И в каком-то другом классе мы его переопределяем, при этом располагая флаги в неправильном порядке:

Компилятор не заметит проблемы, поскольку параметры различаются только по именам, а их типы одинаковы (оба bool).

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

Более распространенная проблема — с использованием bool в конструкторах. Пускай есть класс с двумя конструкторами:

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

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

Перечисления

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

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

C++11 вводит понятие классов перечислений, которые удовлетворяют обоим требованиям. Также мы можем использовать тип bool как базовый тип перечисления; таким образом, мы гарантируем, что перечисление содержит только два возможных значения и имеет размер одного bool. Вначале определяем классы флагов:

Теперь мы можем объявить нашу функцию:

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

И если я поставлю флаги в неправильном порядке, то получу ошибку компиляции из-за несоответствия типов:

Каждый флаг имеет уникальный тип, который исправно работает при прямой передаче (perfect forwarding), и Вы никак не сможете расположить параметры в неправильном порядке в объявлениях функций и переопределениях виртуальных методов.

Но использование перечислений в качестве флагов имеет свою цену. Флаги в некоторой мере похожи на значения типа bool, но классы перечислений не имитируют эту схожесть. Неявные преобразования в bool и обратно не работают (и это хорошо), но явные преобразования тоже не работают, и это проблема. Если мы взглянем еще раз на тело функции process, то поймем, что оно не компилируется:

Мне приходится использовать явное преобразование:

И если мне понадобится логическое выражение с двумя флагами, то оно будет выглядеть еще неадекватнее:

Кроме того, для экземпляра класса перечисления Вы не можете сделать прямую инициализацию из bool:

Опять придется делать явное преобразование:

Это можно считать дополнительной гарантией безопасности, но здесь слишком много явных преобразований. В классах перечислений больше «explicit», чем в конструкторах и операторах преобразования, объявленных как «explicit».

tagged_bool

Из-за проблем, возникающих при использовании bool и классов перечислений, мне пришлось сделать свой собственный инструмент, который называется tagged_bool. Вы можете найти его реализацию здесь. Она совсем небольшая. С ее помощью, классы флагов объявляются вот так:

Вам придется сделать предварительное объявление класса-тэга, такого как «WithValidation_tag». Определение для него писать не нужно. Он используется для создания уникальной специализации шаблона класса tagged_bool. Эта специализация может быть явно преобразована в bool и обратно, а также в другие специализации шаблона tagged_bool, поскольку, как обычно бывает на практике, какой-нибудь bool, передающийся на нижние уровни приложения, становится впоследствии другим флагом с другим именем. Использовать созданные таким образом флаги можно вот так:

Вот и все. tagged_bool — это часть библиотеки Explicit library, которая содержит несколько инструментов, позволяющих более явно выразить Ваши намерения при проектировании интерфейсов.

От переводчика

У Анджея ранее была другая статья про тэги — «Intuitive interface — Part I» от 5 июля 2013 г. (Part 2 так и не появилась на свет, не ищите). Если вкратце, то там поднималась такая проблема:

Когда поведение конструктора зависит от формы скобочек, это само по себе опасно. Кроме того, это делает непонятным вызывающий код:

Что такое 5 и 6? Это будет 5 шестерок или 6 пятерок? Если забыли — идите смотреть документацию.

И хотелось бы иметь еще один конструктор, создающий пустой вектор с заданной capacity: std::vector v(100). К сожалению, конструктор, принимающий один size_t, уже занят — он создает вектор с заданным size’ом, заполненный сконструированными по умолчанию объектами.

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

Анджей пришел к выводу, что реализация вектора в библиотеке STL не вполне удачная. Было бы куда проще, если бы в его конструкторах использовались тэги:

Применительно к настоящей статье, это выглядело бы так:

Разница в том, что тэг и значение теперь объединены в один объект. В статье «Competing constructors» от 29 июля 2016 г. Анджей мимоходом написал, что ему не нравится идея такого объединения.

Теперь это уже не тэги, а полноценные объекты. Кому-нибудь может прийти в голову положить их в контейнер:

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

С bool, однако, эта угроза не так страшна. Конструкторы контейнеров STL не принимают bool, иначе как в составе initializer_list’ов. Видимо, поэтому Анджей и решился в этот раз объединить тэг и значение.

Напоследок приведу перевод нескольких комментариев к статье.

Комментарии

kszatan
February 17, 2017 at 11:36 am
Я бы в первую очередь подумал об избавлении от всех этих флагов и вывел бы код для подтверждений (validations) и нового/старого движка (new/old engine) в отдельные классы, чтобы передавать их как аргументы. Функция «process» и так уже делает слишком много.

Andrzej Krzemieński
February 17, 2017 at 12:03 pm
В простых случаях отказ от любых флагов действительно может оказаться лучшим выбором. Но когда решение установить флаг принимается несколькими уровнями выше в стеке вызовов, такой рефакторинг может оказаться неосуществимым или непрактичным.

micleowen
February 17, 2017 at 10:41 pm

«explicit» используется в конструкторах с одним параметром.

Andrzej Krzemieński
February 20, 2017 at 8:22 am

Есть основательная причина объявлять почти все конструкторы как «explicit», а не только конструкторы с 1 аргументом (особенно с выходом стандарта C++11). Иногда даже конструктор по умолчанию лучше объявить как «explicit». Кому интересно, советую обратиться вот к этой статье .
===Конец треда===

ARNAUD
February 18, 2017 at 6:39 pm

«Неявные преобразования в bool и обратно не работают (и это хорошо), но явные преобразования тоже не работают, и это проблема»

Не понимаю, что в этом плохого:

Сначала Вы используете общеизвестную возможность языка, и Ваш код прекрасно читается и понимается всеми специалистами по С++. Потом Вы переходите к использованию специального шаблона для автоматического преобразования в bool и обратно? Меня это не убеждает.

И еще. Представьте, что через какое-то время один из параметров перестанет быть bool и сможет принимать значения no_engine, engine_v1, engine_v2… Класс перечисления позволяет сделать такое расширение естественным путем, в отличие от Вашего tagged_bool.

Andrzej Krzemieński
February 20, 2017 at 8:36 am

Вы подняли два вопроса.

И, в случае использования пространств имен:

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

2. Возможность добавить третье состояние

Если Вы предвидите, что в будущем Вам может понадобиться третье состояние, то классы перечислений и правда могут быть предпочтительнее. А могут и не быть. Потому что, когда Вы добавляете третье состояние, все Ваши if’ы продолжают исправно компилироваться, хотя Вы, может быть, желаете их отредактировать, чтобы добавить проверку третьего состояния.

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

mftdev00
March 13, 2017 at 1:05 pm

Я вообще не люблю флаги. Они противоречат принципу единственной ответственности. Делай что-то, если true, делай что-то другое, если false…

Andrzej Krzemieński
March 13, 2017 at 1:10 pm

Согласен. Везде, где это возможно, нужно обходиться без флагов.

SebB
March 21, 2017 at 6:09 pm

Можно ли вместо явного удаления конструкторов для каждого типа:

…просто удалить их для всех типов (кроме bool) разом?

Andrzej Krzemieński
March 22, 2017 at 7:32 am

Я просто не учел такую возможность, когда разрабатывал интерфейс. Может, и полезно было бы это добавить. Но теперь, когда Вы это предложили, я вижу один случай, где это имело бы отрицательный эффект: кто-то может использовать свой собственный (безопасный) логический тип с неявным преобразованием в bool. В этом случае, нам, может быть, нужно позволить этому типу работать с tagged_bool.

Bool flag c что это

Flag variable is used as a signal in programming to let the program know that a certain condition has met. It usually acts as a boolean variable indicating a condition to be either true or false.
Example 1: Check if an array has any even number.

Input : arr[] = <1, 3, 7, 5>
Output : No All numbers are odd.

Input : arr[] = <1, 2, 7, 5>
Output : Yes There is one even number in the array.

We initialize a flag variable as false, then traverse the array. As soon as we find an even element, we set flag as true and break the loop. Finally we return flag.

Using Boolean values in C

C doesn’t have any built-in Boolean types. What’s the best way to use them in C?

Peter Mortensen's user avatar

18 Answers 18

From best to worse:

Option 1 (C99 and newer)

Option 2

Option 3

Option 4

https://amdy.su/wp-admin/options-general.php?page=ad-inserter.php#tab-8

Explanation

  • Option 1 will work only if you use C99 (or newer) and it’s the "standard way" to do it. Choose this if possible.
  • Options 2, 3 and 4 will have in practice the same identical behavior. #2 and #3 don’t use #defines though, which in my opinion is better.

If you are undecided, go with #1!

Andreas Bonini's user avatar

A few thoughts on booleans in C:

I’m old enough that I just use plain int s as my boolean type without any typedefs or special defines or enums for true/false values. If you follow my suggestion below on never comparing against boolean constants, then you only need to use 0/1 to initialize the flags anyway. However, such an approach may be deemed too reactionary in these modern times. In that case, one should definitely use <stdbool.h> since it at least has the benefit of being standardized.

Whatever the boolean constants are called, use them only for initialization. Never ever write something like

These can always be replaced by the clearer

Note that these can actually reasonably and understandably be read out loud.

Give your boolean variables positive names, ie full instead of notfull . The latter leads to code that is difficult to read easily. Compare

Both of the former pair read naturally, while !notfull is awkward to read even as it is, and becomes much worse in more complex boolean expressions.

Boolean arguments should generally be avoided. Consider a function defined like this

Within the body of the function, it is very clear what the argument means since it has a convenient, and hopefully meaningful, name. But, the call sites look like

Here, it’s essentially impossible to tell what the parameter meant without always looking at the function definition or declaration, and it gets much worse as soon if you add even more boolean parameters. I suggest either

In either case, the call site now looks like

which the reader has at least a chance of understanding without dredging up the definition of foo .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *