Борис Пахомов Санкт Петербург бхв петербург 2013 удк 004. 4 Ббк 32. 973. 26018. 2 П
Скачать 17.38 Mb.
|
Рис. 7.10. Результат работы программы листинга Рис 7.11. Физическая структура дерева, построенного программой листинга Битовые поля в структурах Структуры обладают замечательным свойством сих помощью можно задавать битовые поля определять в переменной типа int группы подряд расположенных битов, значения которых можно задавать и с которыми можно работать как с элементами структуры. Описание битовых полей через структуру задается следующим образом struct { unsigned name1: size1; имя поля и его размер unsigned name2: size2; имя поля и его размер unsigned К К имя поля и его размер Это обычное задание шаблона и на нем экземпляра структуры. Размер — это количество битов, расположенных подряд. Сумма всех полей не должна выходить за пределы размера переменной типа int . Если же это случится, то первое битовое поле, превысившее размер переменной int , расположится в следующем участке памяти, отведенном для переменной типа Пример struct { unsigned n1: 1; имя поля и его размер unsigned n2: 2; имя поля и его размер }flags; Здесь определен экземпляр структуры — переменная flags , содержащая два битовых поля одно — однобитовое, другое — двубитовое. На поля можно ссылаться как на отдельные члены структуры flags.n1 , flags.n2 . Эти поля ведут себя подобно целым без знака и могут участвовать в арифметических операциях, применяемых к целым без знака. Глава 7. Работа с указателями и структурами данных Например, для определенных выше полей можно записать flags.n1=1; включен бит flags.n1=0; выключен бит flags.n2=3; включены биты 1,2 поля //(3 в десятичной системе равно 11 в двоичной) Для проверки значения битов можно писать if(flags.n2==1 && flags.n1==0) Категории памяти Каждая объявленная переменная относится к той или иной категории памяти. Ранее мы видели, что переменные бывают глобальные или локальные. Принадлежность к одной из этих категорий определяет время жизни переменной. Действительно, локальные (или каких еще называют, автоматические) переменные "живут, пока они находятся в теле функции, оператора или блока. По выходе из этих конструкций переменная уничтожается ее память становится доступной к перераспределению для других переменных. Глобальные (их еще называют статическими) переменные "живут, пока работает ваша программа. Все функции относятся к глобальным переменным. Язык С, кроме того, имеет ряд спецификаторов категорий памяти, которые явно указывают на это компилятору. Мы уже встречались с некоторыми из них — это спецификаторы extern и. Первый применяется при определении так называемых внешних переменных (они определяются вне вашей программы, например, в подключаемом к ней файле. Внешние переменные тоже являются глобальными. Второй спецификатор позволяет "глобализовать" локальную переменную при выходе из локализующей ее конструкции такая переменная не теряет своего значения, как не теряет его она и при входе в свою конструкцию. К другим спецификаторам категории памяти относятся спецификаторы а, register , typedef . В объявлении переменной вы можете использовать только один спецификатор категории памяти. Если категория памяти в объявлении переменной не указана, то категория принимается равной аи объявление переменной внутри блока, функции или оператора создает автоматические переменные. Переменные, объявленные с категорией будут также отнесены к автоматическим те. локальным. Если переменной при ее объявлении присваивается категория register , то это означает, что при промежуточных операциях с такой переменной ее содержимое станет размещаться не в оперативной памяти, а на регистрах компьютера, что значительно увеличивает скорость работы с такой переменной. Спецификатор typedef фактически вводит синоним типа, давая возможность программисту вводить типы данных с подходящими ему наименованиями, чтобы улучшить читаемость программы. Вот структура его спецификации typedef заменяемые типы void, char, short, int, long, float, double, signed, unsigned, struct, enum] новое имя 140 Часть I. Изучение языка С/С++ Например: typedef char Flag; Здесь тип char заменен синонимом Flag . Теперь в вашей программе можно вместо char везде писать Flag . Например, Flag b=’b’; . Если же вы используете имя переменной, совпадающее с синонимом, определенным потакая переменная, как обычно, должна иметь тип данного. Например int Другой пример typedef struct club { char name[30]; int size, year; } Здесь вся структура заменена синонимом GROUP . Теперь переменные типа структуры club можно объявлять переменными типа GROUP : GROUP a,b,*c; a.size=123; b.year=2007; c= (GROUP *) malloc(sizeof(GROUP)); выделили память под структуру инициализировали указатель) c->name[0]='a'; c->name[1]='b'; //Или: enum days {sun, mon, tues, wed, thur, fri, sat}; ввели тип typedef enum days d; d Или char * cc; замена указателя cc str; str="123"; int i=atoi(str); ГЛАВА Классы в С. Объектно - ориентированное программирование Концепция объектно-ориентированного программирования (ООП) включает в себя понятия объектов, классов, инкапсуляции и наследования. Объект — это некая математически-программно описанная сущность, элемент окружающего нас мира, с которым мы встречаемся в повседневной жизни. Например, ваша конкретная собака, ваш конкретный телевизор, ваш конкретный автомобиль это все объекты. Реальные объекты имеют две характеристики состояние, которое определяется набором свойств объекта, и поведение. Например, собака имеет состояние, определяемое следующим набором ее свойств имя, цвет шерсти, порода, характер и т. д. А ее поведение определяется тем, что она в данный момент может лаять, вилять хвостом и т. д. Поведение объекта определяется набором функций, заданных в объекте, которые здесь называются методами. Класс — это некий чертеж, некий проект, из которого создается объект. По аналогии с простыми переменными, рассмотренными ранее, класс — это некий тип данных, некий шаблон, из которого затем формируется конкретный объект. Если видели, что имеется тип int для описания переменных, то задав int A; мы создаем переменную (можем назвать ее объектом для общности) А. Написав А мы этот объект инициализируем. С классами происходит точно такая же история, как мы в дальнейшем увидим. Класс — это дальнейшее обобщение понятия объекта программирования, с которым приходится работать. В классе заложены свойства и поведение будущего объекта, который получается из класса как из проекта. Например, "Автомобили" — это класс. "Тойота" — это объект класса "Автомобили, конкретное воплощение класса в конкретную модель. Или, например, проект дома серии 135 — это класса сам конкретный дом, построенный по конкретному адресу это объект класса домов серии 135. Таким образом, когда мы смотрим на окружающие нас объекты реального мира в плане их состояния и поведения, то готовы к пониманию объектно-ориентированного программирования. Объектно-ориентированное программирование — это способ программирования с ориентацией на объекты. При таком способе создаются крупные программные образования — классы, куда закладываются общие свойства будущих объектов, 142 Часть I. Изучение языка С/С++ которые станут получаться по определенным правилам из этих классов, куда закладываются варианты поведения будущих объектов через создаваемые в классах параметрические программы (методы классов. Такой способ значительно ускоряет разработку программного обеспечения и облегчает труд программиста. Вспомним, что одним из первых средств автоматизации труда программиста были так называемые стандартные программы, которые выполняли часто встречающиеся действия (например, перевод десятичных чисел в двоичные, вычисление тригонометрических функций и т. п. Такие программы объединялись в библиотеки стандартных программ. Сегодня на более высоком уровне мы имеем библиотеки классов, которые поставляют нашим программам необходимые им объекты (рисунки, фотографии, средства мультимедиа и т. п, тем самым повышая качество и эффективность современного программирования. Например, у вас есть класс Arr , создающий массив, ив самом классе имеются методы для вычисления суммы и среднего значения хранимых в массиве чисел. Вам в вашей программе надо работать с неким массивом чисел. Не имея класса Arr , вам надо было бы объявить массив, проинициализи- ровать его (наполнить его элементами, затем создать функции для вычисления суммы и среднего значения хранимых в массиве чисел, а уже после всего этого приступать к обработке массива и вычислению суммы его элементов и среднего значения этих элементов. Назавтра другой программист может столкнуться с точно такой же проблемой. А если воспользоваться уже готовым классом Arr , то работа с созданием и обработкой массива значительно сокращается из класса создается объект (сам конкретный массива затем обращением к методам класса (примерно, как к элементам знакомых нам структур) вычисляются необходимые величины. Программно в классах задаются элементы, называемые свойствами. Они описывают состояние объекта и хранятся в специальных элементах, называемых полями. Поведение же объекта описывается специальными функциями, которые в классах носят название методов. Когда свойствам класса присваиваются какие-то конкретные значения, тотем самым из класса создается конкретный объект. Объекты создаются специальным методом класса, называемым конструктором. Методы обрабатывают внутренние состояния объекта и обеспечивают механизм взаимодействия между объектами. Например, возьмем класс велосипедов. Свойствами, характеризующими состояния объектов этого класса, будут текущая скорость, текущее состояние переключателя педали (изменение скорости, количество шестеренок и текущая шестеренка, за счет которой скорость изменяется. Методами, которые изменяют состояние велосипеда, будут смена шестеренки, переключение педали и изменение скорости. Весь этот механизм изменения свойств методами класса спрятан внутри самого класса. Такой принцип взаимодействия элементов класса носит название инкапсуляции данных. Это фундаментальный принцип объектно-ориентированного программирования. В чем же фактическая польза от механизма классов-объектов? Во-первых, обеспечивается модульность программирования исходный код объекта написан и поддерживается независимо от исходных кодов других объектов (те. повышается надежность всей задачи, программа которой состоит из цепочки таких независимых модулей. Глава 8. Классы в С. Объектно - ориентированное программирование Во-вторых, механизм работы такого модуля скрыт внутри самого модуля и не отвлекает программиста на выяснение различных мелких деталей алгоритма. В-третьих, имеется возможность многократного использования элемента (как и ко- гда-то многократное использование библиотеки стандартных программ. В-четвертых, обеспечивается легкая сменяемость элементов в общей программе в приложении если такой элемент выходит из строя, его можно легко заменить аналогичным элементом, не разрушая всю задачу. Итак, ООП основано на использовании классов. Использование классов — это основное отличие языка Сот языка С. Классы Существуют разработчики классов и пользователи классов (разработчики приложений если разработчик создает классы, то пользователь манипулирует классами и экземплярами классов. Класс — это обыкновенный тип. Если вы программист, то всегда имеете дело с типами и экземплярами, даже если и не используете эту терминологию. Например, вы создаете различные переменные типа int . Можно сказать, что вы фактически создаете различные экземпляры переменных этого типа. Классы обычно более сложны, чем простые типы данных, но они работают тем же способом. Создавая переменные типа заданного класса, вы создаете экземпляры этого класса, а назначая различные значения экземплярам того же типа (как и переменным типа int ), вы можете выполнять разные задачи. Поэтому наша цель — научиться пользоваться классами для написания приложений, а создание самих классов оставить их разработчикам. Класс — это собрание связанной информации, которая включает в себя и данные, и функции (программы для работы сданными. Эти функции в классах называются методами. Класс — это дальнейшее развитие структур в них тоже объединяются данные разных типов. Это такой же шаблон, под который (как и под структуру) память выделяется только тогда, когда мы создаем "переменную типа этого шаблона. Вспомним, что если у нас была некая структура А, то чтобы работать с ней, мы создавали экземпляр этой структуры а путем объявления А а; , а затем уже работали с экземпляром а. Можно сказать, что мы объявляли переменную а типа А Точно также поступают и для класса если есть класс А (шаблон, под него память не выделяется, то объявляют переменную а типа А путемобъявления А а, после чего можно работать уже как бы с самим классом, а на самом деле — сего экземпляром а .Как и при использовании структур, к членам класса (данными методам) можно обращаться по тем же правилам если объявлено А а, то обращение к члену класса с именем аа будет записываться как а.аа , а если был объявлен указатель на класс (например, как А а, то обращение к члену класса с именем аа будет записываться как а->аа Класс — это конструкция, параметрически определяющая некоторую категорию объектов. Например, может быть класс компьютеров, который объединяет в себе 144 Часть I. Изучение языка С/С++ компьютеры разных марок, разных возможностей. Может быть класс столов столы письменные, обеденные и т. п. Класс столов может делиться на подклассы столы письменные, которые, в свою очередь, могут делиться на столы письменные дубовые и столы письменные древесно-волокнистые и т. д. Мы видим, что классы могут принадлежать некой иерархии классов. В каждом классе определены характеристики тех объектов, которые образуют этот класс. В классе также задаются программы, называемые методами, которые обрабатывают характеристики объектов, принадлежащих данному классу. Поведение объекта в реальном мире определяется его характеристиками. Изменяя значение характеристик, мы получим разное поведение объектов. Когда мы создаем экземпляр класса и определяем значения его конкретных характеристик, то получаем конкретный объект. В составе класса существует специальный метод (те. программа-функция), который формирует экземпляр класса. Этот метод носит название конструктора. В противоположность конструктору, существует программа-деструктор, которая уничтожает экземпляр класса в памяти, освобождает память, которая может использоваться для других программных целей. А если вспомнить, что память — величина небеспредельная, то становится понятной и роль деструктора. Принципы построения классов Основные принципы построения классов — это инкапсуляция, наследование и полиморфизм. Инкапсуляция Инкапсуляция — это принцип объединения в единой конструкции и данных, и программ, обрабатывающих эти данные. В терминологии ООП данные называются членами-данными, а программы, их обрабатывающие (эти программы построены в виде функций, — членами-функциями, или методами. Такой подход позволяет максимально изолировать объект, получаемый из класса, от внешнего воздействия, что приводит к высокой надежности программ, использующих объекты. С другой стороны, классы используются так, как ранее использовались стандартные программы, только с еще большей эффективностью в самых разных приложениях, что значительно повышает производительность труда программиста. При добавлении новых характеристик классам программы, ранее использовавшие объекты, построенные из них, остаются без изменений. В VC++ введено понятие компонентов — специальных классов, в которых объекты определяются такими характеристиками, как свойства, события и методы. Причем, в отличие от работы с обычными классами, при работе в VC++ возможно манипулировать видом и функциональным поведением компонентов и на стадии проектирования приложения, ив момент его выполнения. Например, в VC++ существует компонент "форма" (класс Form ) и компонент "кнопка" (классу которых есть свои свойства, методы и события. Если при проектировании приложения Глава 8. Классы в С. Объектно - ориентированное программирование в форму поместить две кнопки, то с помощью задания двух разных значений свойствам кнопок название кнопки) и Visible (значения false и true определяют видимость кнопки при исполнении приложения) вы получаете два экземпляра, которые ведут себя по-разному: первая кнопка при выполнении программы будет невидима на форме, а вторая останется видимой. При помощи события компонент сообщает пользователю, что на него произведено определенное воздействие (например, для компонента "кнопка" событием может быть нажатие кнопки — щелчок кнопкой мыши, а методы служат для обработки реакции компонента на события. Наследование Наследование — второй принцип построения классов. Мы видели, что классы, в общем случае, могут составлять иерархию один класс получается из другого, на основании другого получается третий и т. д. То есть речь идет о том, что ив классах существуют родители и дети, бабушки с дедушками, их внуки и т. д. Наследование предполагает, что все характеристики класса-родителя присваиваются клас- су-потомку. После этого потомку при необходимости добавляют новые характеристики. Иногда некоторые методы в классе-потомке, полученном от предков, переопределяются, те. наполняются новым содержанием. Наследование используется не только при разработке классов, но и при проектировании приложения. Например, весть класс Label (метка. Если поместить экземпляр этой метки на форму (экземпляр Form ), то свойство "шрифт" метки примет значение свойства "шрифт" из экземпляра Form . Меняя параметры шрифта у родителя ( Form ), мы добиваемся изменения этих параметров у потомков (наследников. То есть метка автоматически будет наследовать это свойство от экземпляра Form , на который она помещена. Это же относится, например, и к классу кнопка. Пример только что сказанного показан на рис. 8.1, из которого видно, как изменяется шрифт компонентов Кнопка и Метка в зависимости от изменения шрифта на форме. Рис 8.1. Наследование свойства "шрифт" потомками от родителя Мы не ставим своей целью разработку классов (как было отмечено вначале этой главы, но знать основные данные классов необходимо, т. к. иначе невозможно ими пользоваться при построении приложений в среде VC++. 146 Часть I. Изучение языка С/С++ Рассмотрим структуру базового класса, из которого могут создаваться классы- потомки. Объявление класса представлено в листинге 8.1. Листинг 8.1 с имя { private: Имя секции. Данные и методы, помещенные в эту секцию, будут доступны только методам этого класса. Доступ к ним методам производных классов запрещен Приватные данные Приватные конструкторы Приватные методы protected: Имя секции. Данные и методы, помещенные в эту секцию, будут доступны методам этого класса и производным от него, те. его потомкам защищенные данные защищенные конструкторы защищенные методы public: /* Имя секции. Данные и методы, помещенные в эту секцию, будут доступны методам всех классов общедоступные данные общедоступные конструкторы общедоступный деструктор> общедоступные методы }; обратите внимание, что также заканчивается и объявление структуры Глядя на то, как объявляется класс, вспомним пример из разд. "Структуры. Объявление структур" главы 7, где речь шла о том, что для структур в языке С+ можно задавать секции private , public , Эти же секции можно задавать и для классов. Как и для структур, в этих секциях можно определять функции (в классах это методы, а вызывать методы на выполнение можно только в соответствии стем, в какой секции находится функция. Атрибуты private , public , protected называются атрибутами доступа к членам класса. В классах методы вызываются также, как если бы они находились в структуре имя (экземпляра).f(фактические параметры функции); А все эти сходства оттого, что в С+ структуры рассматриваются тоже как классы. |