Лабораторные работы МИРЭА 2014 (1017113), страница 7
Текст из файла (страница 7)
Точно так же поступают и для класса: если есть класс А (шаблон, под него память не выделяется), то объявляют переменную типа А путем объявления А а; , после чего можно работать уже как бы с самим классом, а на самом деле – с его экземпляром а. Как и при использовании структур, к членам класса (данным и методам) можно обращаться по тем же правилам: если объявлено А а;, то обращение к члену класса с именем аа будет записываться как а.аа, а если был объявлен указатель на класс (например, как А *а;), то обращение к члену класса с именем аа будет записываться как а->аа.
Класс – это конструкция, параметрически определяющая некоторую категорию объектов. Например, может быть класс компьютеров, который объединяет в себе компьютеры разных марок, разных возможностей. Может быть класс столов: столы письменные, обеденные и т.п. Класс столов может делиться на подклассы: столы письменные, которые, в свою очередь могут делиться на столы письменные дубовые и столы письменные древесноволокнистые и т.д. Мы видим, что классы могут принадлежать некой иерархии классов. В каждом классе определены характеристики тех объектов, которые образуют этот класс.
В классе также задаются программы, называемые методами, которые обрабатывают характеристики объектов, принадлежащих данному классу. Поведение объекта в реальном мире определяется его характеристиками. Изменяя значение характеристик, мы получаем разное поведение объектов. Когда мы создаем экземпляр класса и определяем значения его конкретных характеристик, то получаем конкретный объект.
В составе класса существует специальный метод (т.е. программа-функция), который формирует экземпляр класса. Этот метод носит название конструктора. В противоположность конструктору, существует программа – деструктор, которая уничтожает экземпляр класса в памяти, освобождает память, которая может использоваться для других программных целей. А если вспомнить, что память – величина не беспредельная, то становится понятной и роль деструктора.
Принципы построения классов.
Основные принципы построения классов это – инкапсуляция, наследование и полиморфизм.
Инкапсуляция
Инкапсуляция – это принцип объединения в единой конструкции и данных, и программ, обрабатывающих эти данные. В терминологии ООП данные называются членами-данными, а программы, их обрабатывающие (эти программы построены в виде функций), - членами- функциями (или методами).
Такой подход позволяет максимально изолировать объект, получаемый из класса, от внешнего воздействия, что приводит к высокой надежности программ, использующих объекты. С другой стороны, классы используются так, как ранее использовались стандартные программы, только с еще большей эффективностью в самых разных приложениях, что значительно повышает производительность труда программиста. При добавлении новых характеристик классам программы, ранее использовавшие объекты, построенные из них, остаются без изменений.
В Visual C++ введено понятие компонентов – специальных классов, в которых объекты определяются такими характеристиками, как свойства, события и методы. Причем, в отличие от работы с обычными классами, при работе в Visual C++ возможно манипулировать видом и функциональным поведением компонентов и на стадии проектирования приложения, и в момент его выполнения.
Например, в Visual C++ существует компонент «форма» (класс Form) и компонент «кнопка» (класс Button), у которых есть свои свойства, методы и события. Если при проектировании приложения в форму поместить две кнопки, то с помощью определения двух разных значений свойствам кнопок Text (название кнопки) и Visible (значения false и true определяют видимость кнопки при исполнении приложения) вы получаете два экземпляра, которые ведут себя по-разному: первая кнопка при выполнении программы будет невидима в форме, а вторая останется видимой. При помощи события компонент сообщает пользователю, что на него произведено определенное воздействие (например, для компонента «кнопка» событием может быть нажатие кнопки – щелчок кнопкой мыши), а методы служат для обработки реакции компонента на события.
Наследование.
Наследование – второй принцип построения классов. Мы видели, что классы в общем случае могут составлять иерархию: один класс получается из другого, на основании другого получается третий и т.д. То есть речь идет о том, что и в классах существуют родители и дети, бабушки с дедушками, их внуки и т.д. наследование предполагает, что все характеристики класса-родителя присваиваются классу-потомку. После этого потомку при необходимости добавляют новые характеристики. Иногда некоторые методы в классе-потомке, полученном от предков, переопределяются, т.е. наполняются новым содержанием.
Рассмотрим структуру базового класса, из которого могут создаваться классы-потомки.
class <имя>
{
private: /* Имя секции. Данные и методы, помещенные в эту секцию будут доступны только методам этого класса. Доступ к ним методом производных классов запрещен*/
<Приватные данные>
<Приватные конструкторы>
<Приватные методы>
protected: /* Имя секции. Данные и методы, помещенные в эту секцию будут доступны только методам этого класса и производным от него, т.е. его потомкам */
<Защищенные данные >
< Защищенные конструкторы >
< Защищенные методы >
public: /* Имя секции. Данные и методы, помещенные в эту секцию будут доступны методам всех классов */
<Общедоступные данные >
< Общедоступные конструкторы >
< Общедоступные деструкторы >
< Общедоступные методы >
}; /*обратите внимание, на точку с запятой */
В секциях private, protected, public можно определять функции (в классах – это методы), а вызывать методы на выполнение можно только в соответствии с тем, в какой секции находится функция. Атрибуты private, protected, public называются атрибутами доступа к членам класса. В классах методы вызываются так же, как если бы они находились в структуре:
имя_объекта.имя_метода (фактические параметры функции);
Полиморфизм
Полиморфизм – это третий принцип, лежащий в основе создания класса. При полиморфизме (дословно: многоформие) родственные объекты (т.е. происходящие от общего родителя) могут вести себя по-разному в зависимости от ситуации, возникающей в момент выполнения программы. Чтобы добиться полиморфизма, надо иметь возможность один и тот же метод в классе-родителе переопределить в классе-потомке. Например, в С++ все классы имеют общего прародителя – это класс Object. В этом классе определен метод draw (рисовать фигуру). Классы, рисующие различные фигуры и произошедшие от Object, - родственные классы. Каждый из них определяет рисование своей фигуры методом draw, унаследованным от Object: точку, линию, прямоугольник, окружность и т.д. Но все фигуры разные, хотя метод общий. Но этот метод draw в каждом из классов-потомков переопределен, т.е. в каждом классе-потомке ему назначена другая функциональность.
Полиморфизм достигается за счет того, что методам из класса-родителя позволено выполняться в классе-потомке, а там оставляют только имя, но при этом дают ему необходимую для данного класса функциональность. Такие методы должны объявляться в обоих классах с атрибутом virtual, записываемым перед атрибутом «возвращаемый тип данных». Если функция имеет атрибут virtual, то она может быть переопределена в классе-потомке, даже если количество и тип ее аргументов такие, что и функции базового класса. Переопределенная функция отменяет функцию базового класса.
Кроме атрибута virtual, у методов существует атрибут friend. Методы с таким атрибутом, расположенным (как и атрибут virtual) в объявлении метода перед указанием типа возвращаемых данных, называются дружественными. Метод, объявленный с атрибутом friend, имеет полный доступ к членам класса, расположенным в секциях private и protected, даже если этот метод – не член этого класса. Это справедливо и для классов: внешний класс (т.е. его методы) имеет полный доступ к классу, который объявляет этот внешний класс дружественным. Во всех остальных аспектах дружественный метод – это обычный метод. Подобные методы из внешних классов, имея доступ к секциям private и protected, могут решать задачи, реализация которых с помощью методов-членов данного класса затруднительна или даже невозможна.
Указатель this
Каждый объект содержит свой экземпляр полей класса. Методы класса находятся в памяти в единственном экземпляре и используются всеми объектами совместно, поэтому необходимо обеспечить работу методов с полями именно того объекта, для которого они были вызваны. Это обеспечивается передачей в функцию скрытого параметра this, в котором хранится константный указатель на вызвавший функцию объект. Указатель this неявно используется внутри метода для ссылок на элементы объекта. В явном виде этот указатель применяется в основном для возвращения из метода указателя (return this;) или ссылки (return *this;) на вызвавший объект.
Указатель this можно также применять для идентификации поля класса в том случае, когда его имя совпадает с именем формального параметра метода. Другой способ идентификации поля использует операцию доступа к области видимости.
Дружественная функция
Дружественная функция объявляется внутри класса, к элементам которого ей нужен доступ, с ключевым словом friend. В качестве параметра ей должен передаваться объект или ссылка на объект класса, поскольку указатель this ей не передается. Одна функция может "дружить" сразу с несколькими классами.
Дружественная функция может быть обычной функцией или методом другого ранее определенного класса. На нее не распространяется действие спецификаторов доступа, место размещения ее объявления в классе безразлично.
В качестве примера ниже приведено описание двух функций, дружественных классу monster. Функция kill являетсяметодом класса hero, а функция steal_ammo не принадлежит ни одному классу. Обеим функциям в качестве параметра передается ссылка на объект класса monster.
class monster; // Предварительное объявление класса
class hero
{
...
void kill(monster &);
};
class monster
{
...
friend int steal_ammo(monster &);
/* Класс hero должен быть определен ранее */
friend void hero::kill(monster &);
};
int steal_ammo(monster &M){return --M.ammo;}
void hero::kill(monster &M){M.health = 0; M.ammo = 0;}
Дружественный класс
Если все методы какого-либо класса должны иметь доступ к скрытым полям другого, весь класс объявляется дружественным с помощью ключевого слова friend. В приведенном ниже примере класс mistress объявляется дружественным классу hero:
class hero{
... friend class mistress;}
class mistress{
... void f1();
void f2();}
Функции f1 и f2 являются дружественными по отношению к классу hero (хотя и описаны без ключевого слова friend) и имеют доступ ко всем его полям.
Объявление friend не является спецификатором доступа и не наследуется. Обратите внимание на то, что класс сам определяет, какие функции и классы являются дружественными, а какие нет.
Примеры создания классов
Приведем примеры трех программ, в которых объявлены и использованы простейшие классы.
Пример_1:
//KIIUT.cpp: главный файл проекта.
#include <iostream.h>
#include <conio.h>
class A
{
protected:
int x;/* к этим данным имеют доступ только методы данного класса и производных*/
int y;
public: