Листинг 21.8. Использование битовых полей
1: #include <iostream.h>
2: #include <string.h>
3:
4: enum STATUS { FullTime, PartTime };
5: enum GRADLEVEL { UnderGrad, Grad };
6: enum HOUSING { Dorm, OffCampus };
7: enum FOODPLAN { OneMeal, AllMeals, WeekEnds, NoMeals };
8:
9: class student
10: {
11: public:
12: student():
13: myStatus(FullTime),
14: myGradLevel(UnderGrad),
15: myHousing(Dorm),
16: myFoodPlan(NoMeals)
17: { }
18: ~student() { }
19: STATUS GetStatus();
20: void SetStatus(STATUS);
21: unsigned GetPlan() { return myFoodPlan; }
22:
23: private:
24: unsigned myStatus: 1;
25: unsigned myGradLevel: 1;
26: unsigned myHousing: 1;
27: unsigned myFoodPlan: 2;
28: };
29:
30: STATUS student::GetStatus()
31: {
32: if (myStatus)
33: return FullTime;
34: else
35: return PartTime;
36: }
37: void student::SetStatus(STATUS theStatus)
38: {
39: myStatus = theStatus;
40: }
41:
42:
43: int main()
44: {
45: student Jim;
46:
47: if (Jim.GetStatus()== PartTime)
48: cout << "Jim is part time" << endl;
49: else
50: cout << "Jim is full time" << endl;
51:
52: Jim.SetStatus(PartTime);
53:
54: if (Jim.GetStatus())
55: cout << "Jim is part time" << endl;
56: else
57: cout << "Jim is full time" << endl;
58:
59: cout << "Jim is on the " ;
60:
61: char Plan[80];
62: switch (Jim.GetPlan())
63: {
64: case OneMeal: strcpy(Plan, "One meal"); break;
65: case AllMeals: strcpy(Plan, "All meals"); break;
66: case WeekEnds: strcpy(Plan, "Weekend meals"); break;
67: case NoMeals: strcpy(Plan, "No Meals");break;
68: default : cout << "Something bad went wrong!n"; break;
69: }
70: cout << Plan << " food plan. " << endl;
71: return 0;
72: }
Результат:
Jim is part time
Jim is full time
Jim is on the No Meals food plan.
Анализ: Строки 4—7 содержат определение нескольких перечислений. Они используются для определения значения битовых полей внутри класса student.
В строках 9—28 объявляется класс student. Несмотря на тривиальность, он интересен тем, что все его данные упакованы в пяти битах. Первый бит определяет, является ли данный студент представителем очной (full time) или заочной (part time) формы обучения. Второй — получил ли этот студент степень бакалавра (UnderGrad). Третий — проживает ли студент в общежитии. И последние два бита определяют, какой из четырех возможных вариантов питания в студенческой столовой выбран студентом.
Методы класса не отличаются ничем особенным от методов любого другого класса, т.е. на них никоим образом не повлиял тот факт, что они написаны для битовых полей, а не для обычных целочисленных значений или перечислений.
Функция-член GetStatus() считывает значение бита и возвращает константу перечисления, но это не обязательное решение. С таким же успехом можно было бы написать вариант, непосредственно возвращающий значение битового поля. Компилятор сам сможет преобразовать битовое значение в константу.
Чтобы убедиться в этом, замените выполнение функции GetStatus() следующим кодом:
STATUS student::GetStatus()
{
return myStatus;
}
При этом в работе программы не произойдет никаких изменений. Вариант, приведенный в листинге, — это дань ясности при чтении кода, а на результат работы компилятора это изменение никак не повлияет.
Обратите внимание на то, что строка 47 должна проверить статус студента (full time или part time), а затем вывести соответствующее сообщение. Попробуем выполнить то же самое по-другому:
cout << "Jim is " << Jim.GetStatus() << endl;
В результате выполнения этого выражения будет выведено следующее сообщение:
Jim is 0
Компилятор не сможет перевести константу перечисления PartTime в соответствующую строку текста.
В строке 62 программы определяется вариант питания студента и для каждого возможного значения соответствующее сообщение помещается в буфер, а затем выводится в строке 70. Опять-таки заметим, что конструкцию с оператором switch можно было бы написать следующим образом:
case 0: strcpy(Plan,"One meal"); break;
case 1: strcpy(Plan,"All meals"); break;
case 2: strcpy(Plan,"Weekend meals"); break;
case 3: strcpy(Plan,"NoMeals"); break;
Самое важное в использовании битовых полей то, что клиент класса не должен беспокоиться насчет способа хранения данных. Поскольку битовые поля относятся к скрытым данным, вы можете свободно изменить их впоследствии, при этом никаких изменений интерфейса не потребуется.
Стиль программирования
Как уже упоминалось в этой книге, в программе важно придерживаться одного принятого стиля, хотя в целом не имеет значения, какому именно стилю вы отдаете предпочтение. Соблюдение определенного стиля существенно облегчает чтение и анализ программы.
Следующие рекомендации совершенно ни к чему вас не обязывают. Они основаны на ряд принципов, которых я придерживаюсь при работе над проектами и которые нахожу полезными. Вы можете выработать собственные правила, но приведенные ниже помогут выделить основные моменты, на которые следует обратить внимание.
Хотя в жизни чрезмерная пунктуальность нас раздражает, тем не менее строгое соблюдение стиля при написании программы поможет вам и вашим коллегам эффективнее использовать и модернизировать однажды написанный код. Постарайтесь выработать собственный стиль программирования и затем относитесь к нему так, как к уголовному кодексу, нарушение которого карается законом.
Отступы
Отступ табуляции должен составлять четыре пробела. Убедитесь в том, что ваш редактор преобразует каждую табуляцию в четыре пробела.
Фигурные скобки
Способ выравнивания фигурных скобок вызывает, возможно, самые бурные споры между программистами C++ и С. Я лично придерживаюсь следующих правил:
• пара фигурных скобок должна быть выровнена по вертикали;
• фигурные скобки первого уровня в определении или объявлении должны быть выровнены по левому полю. Все строки блока объявления или определения записываются с отступом. Все вложенные пары фигурных скобок должны быть выровнены по одной линии со строкой программы, за которой начинается этот блок;
• строки блока никогда не должны находиться на одной линии с фигурными скобками, обрамляющими этот блок, например:
if (condition==true)
{
j = k;
SomeFunction();
}
m++;
Длинные строки
Удерживайте ширину строк в таких пределах, чтобы они помещались на экране. Код, который "убегает" вправо, можно легко пропустить, а горизонтальная прокрутка всегда раздражает. При разбиении строки для следующих строк делайте отступы. Старайтесь разбивать строку, следуя логике и здравому смыслу. Оставляйте оператор в конце предыдущей строки (а не в начале следующей), чтобы было понятно, что данная строка является продолжением предыдущей.
В языке C++ функции часто оказываются более короткими, чем в С, но по- прежнему остается в силе старый добрый совет: старайтесь сохранять свои функции достаточно короткими, чтобы всю функцию можно было увидеть на экране.
Конструкции с оператором switch
В конструкциях с оператором switch используйте отступы таким образом, чтобы четко выделить различные варианты:
switch(переменная)
{
case Значение_1:
Oперация_1();
break;
case Значение_2:
Операция_2();
break;
default;
assert("Ошибочное действие");
break;
}
Текст программы
Чтобы создавать программы, которые будут простыми для чтения, воспользуйтесь следующими советами. Если код просто читать, его нетрудно будет и поддерживать.
• Используйте пробелы, чтобы сделать текст программы более разборчивым.
• Не используйте пробелы внутри ссылок на объекты и массивы (., ->, [ ]).
• Унарные операторы логически связаны со своими операндами, поэтому не ставьте между ними пробелов. К унарным операторам относятся следующие: !, ^, ++, --, -, * (для указателей), & (преобразования типа), sizeof.
• Бинарные операторы должны иметь пробелы с обеих сторон: +, =, *, /, %, >>, <<, <, >, ==, !=, &, I, &&, ||, ?:, -=, += И Т.Д.
• Не используйте отсутствие пробелов для обозначения приоритета (4+ 3*2).
• Ставьте пробелы после запятых и точек с запятой, но не перед ними.
• Круглые скобки не должны отделяться пробелами от заключенных в них параметров.
• Ключевые слова, такие как if, следует отделять пробелами: if (а == b).
• Текст комментария следует отделять пробелом от символов //.
• Размещайте спецификаторы указателей и ссылок рядом с именем типа, а не с именем переменной, т.е.