Основы информатики Классы Заикин Олег Сергеевич zaikin.icc@gmail.com Объектно-ориентированное программирование Инкапсуляция - скрытие деталей реализации, обеспечение доступности главного путем помещения всего мешающего, второстепенного в некую условную капсулу (чёрный ящик). Наследование позволяет создавать иерархию объектов, в которой объекты-потомки наследуют все свойства своих предков. Свойства при наследовании повторно не описываются. Кроме унаследованных, потомок обладает собственными свойствами. Объект в C++ может иметь сколько угодно потомков и предков. Полиморфизм - возможность определения единого по имени действия, применимого ко всем объектам иерархии, причем каждый объект реализует это действие собственным способом. Класс Объектно-ориентированное программирование основано на понятии класса. Класс – абстрактный тип данных в объектноориентированном программировании. Объект – экземпляр класса. Класс задает формат объекта. Класс – абстракция, которая реально не существует, пока не будет создан объект этого класса. Класс Объявляя класс, Вы задаете данные и код, который выполняется над данными. Простой класс может содержать только данные или только код, но обычно содержится и то и другое. Класс в итоге определяется как список своих членов, а именно полей (свойств) и методов/функций/процедур. Класс Объявление класса начинается с ключевого слова class и синтаксически подобно объявлению структуры. По умолчанию члены класса являются закрытыми (private), т.е. к ним могут получить доступ только функции этого же класса. Никакие другие части программы этого не могут. Это одно из проявлений инкапсуляции – можно управлять доступом к определенным элементам данных. Для открытости нужно использовать ключевое слово public. class <имя> { private: <описание скрытых извне членов класса> public: <описание доступных извне членов класса> }; Поля класса Поля класса: • могут иметь любой тип, кроме типа этого же класса (но могут быть указателями или ссылками на этот класс); • могут быть описаны с модификатором const. Инициализация полей при описании не допускается. Тело класса определяет отдельную область видимости. Наличие в двух разных классах членов с одинаковыми именами – не ошибка, эти имена относятся к разным областям видимости. Функции класса Функции (методы) класса объявляются в его теле. Функции класса отличается от обычных функций следующим: функции класса имеют право доступа как к открытым, так и к закрытым членам класса, тогда как обычным функциям доступны лишь открытые. Конечно, функции одного класса, как правило, не имеют доступа к данным другого класса. Пример класса Пример. Класс очередь (queue) class queue { int q[100]; int sloc, rloc; void init(); void qput(int i); int qget(); }; Почему класс queue бесполезен? Эквивалентно class queue { private: int q[100]; int sloc, rloc; void init(); void qput(int i); int qget(); }; Класс class queue { int q[100]; int sloc, rloc; public: void init(); void qput(int i); int qget(); }; Теперь к функциям init(), qput() и qget() доступ открытый. Хотя бы к одной функции класса должен быть открытый доступ, иначе невозможно что-либо сделать с полями класса. Класс После создания объект имеет собственную копию данных, которые составляют класс. Объект занимает определенную область памяти. Чтобы получить доступ к открытому члену класса используется оператор «точка», как и при работе со структурами. К закрытым членам класса так обратиться нельзя. Пример queue Q1, Q2; // объявление двух объектов класса queue Q1.init(); // вызов открытой функции Q1.sloc = 3; // ошибка компиляции - попытка обращения к закрытому члену класса. Класс В объявлении класса содержатся только прототипы функций. Чтобы реализовать функцию, необходимо указать к какому классу она принадлежит. Для этого перед именем функции нужно поставить имя класса и “::”. “::” - оператор разрешения области видимости, он квалифицирует имя члена класса вместе с именем его класса. Пример void queue :: qput(int i) { if (sloc == 100) { cout << “Очередь заполнена” << endl; return; } q[sloc++] = i; } Класс Различные классы могут использовать одинаковые имена своих членов. С помощью оператора “::” компилятор определит, к какому классу он относится. Для вызова из функции класса другой функции этого же класса оператор “.” не требуется. Аналогично для обращения к полям класса. Пример void queue :: init() { rloc = sloc = 0; // указывать область видимости не нужно } Класс Обычно описание класса помещают в заголовочный файл (расширение *.h), а реализацию функций класса – в файл c расширением *.cpp. Называться файлы должны одинаково. Файл queue.h class queue { int sloc, rloc; public: void init(int i); }; Файл queue.cpp #include “queue.h”; void queue :: init() { rloc = sloc = 0; } Пример программы Пример программы Закрытые элементы Если rloc и sloc будут public, к чему это может привести? // функция возвращает текущее количество элементов в очереди int queue :: size() { return sloc - rloc; } Присваивание объектов Если есть два объекта одного класса, то один можно присвоить другому. По умолчанию данные поразрядно копируются, в том числе закрытые. queue Q1. Q2; Q1.init(); // Q1.rloc == 0, Q2.rloc неопределен Q2 = Q1; // Q1.rloc == 0, Q2.rloc == 0 Если в классе есть указатель, что произойдет при копировании объектов? Присваивание объектов class myclass { int *a; }; myclass x, y; x.a = new int[2]; y = x; Если в классе есть переменная-указатель, что произойдет при копировании объектов? Конструктор Конструктор – функция, которая автоматически вызывается при создании объекта. Имя конструктора совпадает с именем класса. Используется для инициализации части данных. Если программист не указал ни одного конструктора, компилятор создает его автоматически. Такой конструктор вызывает конструкторы по умолчанию для полей класса и конструкторы по умолчанию базовых классов. Например для класса queue при инициализации переменным rloc и sloc нужно присвоить нулевые значения. В рассмотренном ранее примере инициализация выполняется в функции init(). Конструктор Пример class queue { int q[100]; int sloc, rloc; public: queue(); // конструктор void qput(int i); int qget(); }; queue :: queue() { sloc = rloc = 0; } queue Q1, Q2; // создано 2 объекта, у каждого вызван конструктор Деструктор Деструктор – функция, которая вызывается при разрушении объекта. Нужен для освобождения ранее выделенной для объекта памяти или других действий при разрушении объекта. Имя деструктора – это имя конструктора, перед которым стоит символ ~ class queue { int q[100]; int sloc, rloc; public: queue(); // конструктор ~queue(); // деструктор void qput(int i); int qget(); }; Деструктор Деструктор вызывается автоматически, когда объект выходит из области видимости: для локальных объектов — при выходе из блока, в котором они объявлены; для глобальных — как часть процедуры выхода из main; для объектов, заданных через указатели, деструктор вызывается неявно при использовании операции delete. Деструктор Деструктор можно вызвать явным образом путем указания полностью уточненного имени, например: queue *q; ... q -> ~queue(); Деструктор: не имеет аргументов и возвращаемого значения; Если деструктор явным образом не определен, компилятор автоматически создает пустой деструктор.