|
4.2. КлассыВ языке С/С++ концепция классов стала расширением понятия структуры, но в отличие от структуры, классы предназначены для выражения не только структуры объекта, но и его поведения. То есть благодаря классам мы можем описать объект (его атрибуты и поведение) на языке программирования и заставить его "жить" в программе. Атрибуты будут описаны на языке в виде переменных-членов класса, а поведение (операции, действия) - в виде функций-членов. Благодаря этому, при моделировании оптических явлений и процессов, можно в программировании пользоваться теми же понятиями: оптическая поверхность, оптическая среда, линза, диафрагма, предмет, изображение. На языке С/С++ реализация класса мало чем отличается от реализации структуры, но принято для описания поведения объекта использовать класс, а для хранения разнородных данных (если функции-члены не используются) использовать структуры. Также как для идентификаторов (переменных) и функций существуют понятия определения, объявления, реализации класса. Объявление класса - резервирует имя (лексему) как имя класса, нового типа данных. class Lens; class Beam; class Surface; Объявление класса без его реализации чаще всего используется, когда необходимо использовать тип данных при описании другого класса, но подключение header-файла с полным описанием класса является нерациональным. Определение класса - языковая конструкция, которая определяет переменные и функции члены класса (их имена, набор аргументов). Реализация класса – реализация всех функций членов класса. 4.2.1. Пример 4.3. Класс ЛинзаРассмотрим пример класса Линза. ///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 4.3. Класс Линза // lens.h // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// // проверка на повторное подключение файла #if !defined LENS_H #define LENS_H #include <iostream> #include "paraxial.h" ///////////////////////////////////////////////////////////////////////////// // класс ЛИНЗА class Lens { private: // радиусы кривизны линзы double m_r1, m_r2; // диаметр линзы double m_D; // осевое расстояние double m_d; // показатель преломления double m_n; // параксиальные характеристики Paraxial m_paraxial; public: // конструкторы и деструктор Lens(); Lens(double r1, double r2, double D, double d=0., double n=1.); Lens(double r1, double r2); Lens(const Lens& one); ~Lens(); // установка показателя преломления void Set_n(double n); // получение показателя преломления double Get_n() const; // установка осевого расстояния void Set_d(double d); // получение осевого расстояния double Get_d() const; // ... // получение параксиальных характеристик Paraxial GetParaxial() const; // запись и чтение void write(std::ostream& out) const; void read(std::istream& in); private: // вычисление параксиальных характеристик void CalculateParaxial(); }; ///////////////////////////////////////////////////////////////////////////// // установка показателя преломления inline void Lens::Set_n(double n) { m_n=n; CalculateParaxial(); } ///////////////////////////////////////////////////////////////////////////// // получение показателя преломления inline double Lens::Get_n() const { return m_n; } ///////////////////////////////////////////////////////////////////////////// // установка осевого расстояния inline void Lens::Set_d(double d) { m_d=d; CalculateParaxial(); } ///////////////////////////////////////////////////////////////////////////// // получение осевого расстояния inline double Lens::Get_d() const { return m_d; } ///////////////////////////////////////////////////////////////////////////// // получение параксиальных характеристик inline Paraxial Lens::GetParaxial() const { return m_paraxial; } ///////////////////////////////////////////////////////////////////////////// #endif //defined LENS_H
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 4.3. Класс Линза // paraxial.h // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// // проверка на повторное подключение файла #if !defined PARAXIAL_H #define PARAXIAL_H #include <iostream> ///////////////////////////////////////////////////////////////////////////// // структура параксиальных характеристик struct Paraxial { double F, F_; // фокусные расстояния double SF, SF_; // фокальные отрезки double SH, SH_; // положения главных плоскостей Paraxial(); void write(std::ostream& out) const; }; ///////////////////////////////////////////////////////////////////////////// inline Paraxial::Paraxial() : F(0) , F_(0) , SF(0) , SF_(0) , SH(0) , SH_(0) { } ///////////////////////////////////////////////////////////////////////////// inline void Paraxial::write(std::ostream& out) const { out<<"f="<<F<<" f\'="<<F_<<" Sf="<<SF<<" Sf\'="<<SF_<<" SH="<<SH<<" SH\'="<<SH_<<endl; } ///////////////////////////////////////////////////////////////////////////// #endif //defined LENS_H
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 4.3. Класс Линза // lens.cpp // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// using namespace std; // подключение описания класса #include "lens.h" ///////////////////////////////////////////////////////////////////////////// // конструктор по умолчанию Lens::Lens() : m_r1(0) , m_r2(0) , m_d(0) , m_D(0) , m_n(1.) { CalculateParaxial(); } ///////////////////////////////////////////////////////////////////////////// // полный конструктор Lens::Lens(double r1,double r2,double D,double d,double n) : m_r1(r1) , m_r2(r2) , m_d(d) , m_D(D) , m_n(n) { if(n<1) throw exception("Index of refraction should be greater than 1."); CalculateParaxial(); } ///////////////////////////////////////////////////////////////////////////// // неполный конструктор Lens::Lens(double r1, double r2) : m_r1(r1), m_r2(r2), m_d(2.), m_D(5.), m_n(1.5) { CalculateParaxial(); } ///////////////////////////////////////////////////////////////////////////// // конструктор копирования Lens::Lens(const Lens& l) : m_r1(l.m_r1) , m_r2(l.m_r2) , m_d(l.m_d) , m_D(l.m_D) , m_n(l.m_n) { CalculateParaxial(); } ///////////////////////////////////////////////////////////////////////////// // деструктор Lens::~Lens() { } ///////////////////////////////////////////////////////////////////////////// // запись void Lens::write(ostream& out) const { out<<m_r1<<" "<<m_r2<<" "<<m_d<<" "<<m_D<<" "<<m_n<<endl; } ///////////////////////////////////////////////////////////////////////////// // чтение void Lens::read(istream& in) { in>>m_r1>>m_r2>>m_d>>m_D>>m_n; CalculateParaxial(); } ///////////////////////////////////////////////////////////////////////////// // вычисление параксиальных характеристик void Lens::CalculateParaxial() { // вычисление параксиальных характеристик // для проверки задаются значения 100, -100 m_paraxial.F=100.; m_paraxial.F_=-100.; //m_paraxial.SF=... //m_paraxial.SF_=... //m_paraxial.SH=... //m_paraxial.SH_=... } /////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 4.3. Класс Линза // test_lens.cpp // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// #include <iostream> using namespace std; // подключение описания класса #include "lens.h" ///////////////////////////////////////////////////////////////////// void main() { //---------------------------------------------------------------- // тестирование констуркторов класса // в момент создания экземпляров класса вызывается конструктор по умолчанию Lens lens1; Lens lens3[10]; // вызывается полный конструктор Lens lens4(200., -200., 2., 5., 1.5); // вызывается неполный конструктор Lens lens5(100,-100, 66.); //---------------------------------------------------------------- // тестирование доступа к членам класса // доступ к членам класса осуществляется по оператору "." lens5.Set_n(1.6); // создание указателя на экзепляр класса Lens* lens6=&lens5; // если объявлен указатель на экземпляр класса, // доступ к членам класса осуществляется по оператору "->" double n=lens6->Get_n(); // вывод на экран значений показателя преломления // обращение к private членам осуществляется при помощи функций-селекторов cout<<n<<" "<<lens5.Get_n()<<" "<<lens6->Get_n()<<endl; // cout<<lens5.m_n; // m_n - private-член (ошибка) // cout<<lens6->m_n; // m_n - private-член (ошибка) // cout<<lens6.Get_d(); // lens6 - указатель (ошибка) // вывод всех параметров линзы на экран lens5.write(cout); // вывод на экран параксиальных характеристик линзы Paraxial parax; // объявление экземпляра параксиальных характеристик parax=lens4.GetParaxial(); parax.write(cout); //---------------------------------------------------------------- // в случае возникновения исключительной ситуации внутри блока try // управление переходит к блоку catch try { Lens lens7(100., -100., 50., 5., 0.); parax=lens7.GetParaxial(); parax.write(cout); } // блок catch - обработка ошибки catch(exception& error) { // вывод на экран сообщения об ошибке cout<<error.what()<<endl; } } ///////////////////////////////////////////////////////////////////// Разберем подробнее этот пример. 4.2.2. Директивы препроцессору # if ! defined, # endif (проверка на повторное подключение)Обратите внимание, что определение класса заключено в следующую конструкцию, представляющую собой директиву препроцессору: // проверка на повторное подключение файла #if !defined LENS_H #define LENS_H // ... #endif //defined LENS_H Дело в том, что когда программа содержит большое число заголовочных файлов, которые тоже подключают какие-то другие заголовочные файлы, может возникнуть ситуация, когда один и тот же файл подключается несколько раз, что приводит к ошибке. Данная конструкция предотвращает включение кода, находящегося между # if ! defined и # endif. Если заголовок ранее не включался в исходный файл, директива #define определяет имя LENS _ H и далее подключит остальное содержимое заголовочного файла. Если этот файл уже подключался ранее, LENS _ H уже определено и содержимое заголовочного файла повторно не подключается. Обычно имя константы в этих препроцессорных директивах является именем заголовочного файла в верхнем регистре с заменой точки на символ подчеркивания. 4.2.3. Тип доступа к членам класса
При описании класса для описания доступа к переменным и функциям-членам класса используются ключевые слова private, protected, public. Переменные и функции, описанные как private будут доступны только функциям-членам класса, к ним невозможно обратиться извне класса через оператор "." или "->". Переменные и функции, описанные как public, будут доступны и внутри класса, и снаружи. Класс видимости protected играет важную роль при наследовании: если к некоторым private-членам класса необходимо получать доступ из классов наследников, то они должны быть помечены как protected. Подробно о наследовании см.главу 5. Таким образом, public-функции и переменные называются интерфейсом и позволяют управлять состоянием и поведением класса, а то, что относится к внутренней реализации, должно быть скрыто посредством private. Посредством сокрытия части информации об объекте в нём самом, выполняется один из принципов ООП – инкапсуляция. Обычно скрывается его внутреннее устройство (набор атрибутов и реализация методов). Но внешнее поведение (public-члены) сразу прорабатываются настолько подробно, чтобы интерфейс объекта не изменялся. 4.2.4. Принципы объектно-ориентированного проектированияХотя подробно объектно-ориентированное проектирование в данном курсе не рассматривается, общее представление о принципах ООП может помочь в понимании особенностей реализации классов на языке С++. Принципы ООП:
4.2.5. Типы функций-членов классаПоведение описывается набором действий, которые можно совершить над объектом или сам объект может совершить. Что происходит: чтение-запись, выполнение любых вычислений, порождение событий или реакция на те или иные события происходящие в моделируемой системе. Можно выделить следующие группы действий (функций-членов класса):
При реализации функций-членов класса, оператор :: показывает принадлежность функции к классу. При реализации всех функций-членов класса к имени функции добавляет <имя класса::>, в нашем случае Lens::. Например: void Lens::CalculateParaxial()
{
}
|