Опубликован: 10.10.2006 | Уровень: специалист | Доступ: платный
Лекция 5:

Классы

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

5.1 Введение и краткий обзор

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

Тип есть вполне конкретное представление некоторого понятия. Например, в С++ тип float с операциями +, -, * и т.д. является хотя и ограниченным, но конкретным представлением математического понятия вещественного числа. Новый тип создается для того, чтобы стать специальным и конкретным представлением понятия, которое не находит прямого и естественного отражения среди встроенных типов. Например, в программе из области телефонной связи можно ввести тип trunk_module (линия-связи), в видеоигре - тип explosion (взрыв), а в программе, обрабатывающей текст, - тип list_of_paragraphs (список-параграфов). Обычно проще понимать и изменять программу, в которой типы хорошо представляют используемые в задаче понятия. Удачно подобранное множество пользовательских типов делает программу более ясной. Оно позволяет транслятору обнаруживать недопустимое использование объектов, которое в противном случае останется невыявленным до отладки программы.

Главное в определении нового типа - это отделить несущественные детали реализации (например, расположение данных в объекте нового типа) от тех его характеристик, которые существенны для правильного его использования (например, полный список функций, имеющих доступ к данным). Такое разделение обеспечивается тем, что вся работа со структурой данных и внутрение, служебные операции над нею доступны только через специальный интерфейс (через "одно горло").

Лекция состоит из четырех частей:

\S 5.2 Классы и члены. Здесь вводится основное понятие пользовательского типа, называемого классом. Доступ к объектам класса может ограничиваться множеством функций, описания которых входят в описание класса. Эти функции называются функциями-членами и друзьями. Для создания объектов класса используются специальные функции-члены, называемые конструкторами. Можно описать специальную функцию-член для удаления объектов класса при его уничтожении. Такая функция называется деструктором.

\S 5.3 Интерфейсы и реализации. Здесь приводятся два примера разработки, реализации и использования классов.

\S 5.4 Дополнительные свойства классов. Здесь приводится много дополнительных подробностей о классах. Показано, как функции, не являющейся членом класса, предоставить доступ к его частной части. Такую функцию называют другом класса. Вводятся понятия статических членов класса и указателей на члены класса. Здесь же показано, как определить дискриминирующее объединение.

\S 5.5 Конструкторы и деструкторы. Объект может создаваться как автоматический, статический или как объект в свободной памяти. Кроме того, объект может быть членом некоторого агрегата (массива или другого класса), который тоже можно размещать одним из этих трех способов. Подробно объясняется использование конструкторов и деструкторов, описывается применение определяемых пользователем функций размещения в свободной памяти и функций освобождения памяти.

5.2 Классы и члены

Класс - это пользовательский тип. Этот раздел знакомит с основными средствами определения класса, создания его объектов, работы с такими объектами и, наконец, удаления этих объектов после использования.

5.2.1 Функции-члены

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

struct date { int month, day, year; };
date today;
void set_date(date*, int, int, int);
void next_date(date*);
void print_date(const date*);
// ...

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

struct date {
int month, day, year;

void set(int, int, int);
void get(int*, int* int*);
void next();
void print();
};

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

date today;
date my_birthday;

void f()
{
	my_birthday.set(30,12,1950);
	today.set(1,18,1991);

	my_birthday.print();
	today.next();
}

Поскольку разные структуры могут иметь функции-члены с одинаковыми именами, при определении функции-члена нужно указывать имя структуры:

void date::next()
{
   if (++day > 28 ) {
     // здесь сложный вариант
  }
}

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

5.2.2 Классы

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

class date {
   int month, day, year;
public:
   void set(int, int, int);
   void get(int*, int*, int*);
   void next();
   void print()
};

Служебное слово public (общий) разбивает описание класса на две части. Имена, описанные в первой частной (private) части класса, могут использоваться только в функциях-членах. Вторая - общая часть - представляет собой интерфейс с объектами класса. Поэтому структура - это такой класс, в котором по определению все члены являются общими. Функции-члены класса определяются и используются точно так же, как было показано в предыдущем разделе:

void date::print()     // печать даты  в принятом в США виде
{
  cout << month << '/' << day << '/' << year ;
}

Однако от функций не членов частные члены класса date уже ограждены:

void backdate()
{
  today.day--;    // ошибка
}

Есть ряд преимуществ в том, что доступ к структуре данных ограничен явно указанным списком функций. Любая ошибка в дате (например, December, 36, 1985) могла быть внесена только функцией-членом, поэтому первая стадия отладки - локализация ошибки - происходит даже до первого пуска программы. Это только частный случай общего правила: любое изменение в поведении типа date может и должно вызываться изменениями в его членах. Другое преимущество в том, что потенциальному пользователю класса для работы с ним достаточно знать только определения функций-членов.

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

Равиль Ярупов
Равиль Ярупов
Федор Антонов
Федор Антонов

Здравствуйте!

Записался на ваш курс, но не понимаю как произвести оплату.

Надо ли писать заявление и, если да, то куда отправлять?

как я получу диплом о профессиональной переподготовке?

Евгений Чаленко
Евгений Чаленко
Россия, Новокузнецк, НФИ КемГУ
Антон Свитенков
Антон Свитенков
Беларусь, Речица