straustrup2 (852740), страница 85
Текст из файла (страница 85)
Например:class myclass {// representationpublic:void f();T1 g(T2);// ...};extern "C" { // map myclass into C callable functions:void myclass_f(myclass* p) { p->f(); }T1 myclass_g(myclass* p, T2 a) { return p->g(a); }// ...};В С-программе следует определить эти функции в заголовочном файле следующим образом:// in C header fileextern void myclass_f(struct myclass*);extern T1 myclass_g(struct myclass*, T2);Такой подход позволяет разработчику на С++, если у него уже есть запас программ, написанных наязыках, в которых отсутствуют понятия абстракции данных и иерархии классов, постепенноприобщаться к этим понятиям, даже при том требовании, что окончательную версии программы можнобудет вызывать из традиционных процедурных языков.12.2 КлассыОсновное положение объектно-ориентированного проектирования и программирования заключается втом, что программа служит моделью некоторых понятий реальности.
Классы в программе представляютосновные понятия области приложения и, в частности, основные понятия самого процессамоделирования реальности. Объекты классов представляют предметы реального мира и продуктыпроцесса реализации.Мы рассмотрим структуру программы с точки зрения следующих взаимоотношений между классами:-отношения наследования,-отношения принадлежности,-отношения использования и-запрограммированные отношения.При рассмотрении этих отношений неявно предполагается, что их анализ является узловым моментомв проекте системы. В $$12.4 исследуются свойства, которые делают класс и его интерфейс полезнымидля представления понятий.
Вообще говоря, в идеале, зависимость класса от остального мира должнабыть минимальна и четко определена, а сам класс должен через интерфейс открывать лишьминимальный объем информации для остального мира.Подчеркнем, что класс в С++ является типом, поэтому сами классы и взаимоотношения между ними314Бьерн Страуструп.Язык программирования С++обеспечены значительной поддержкой со стороны транслятора и в общем случае поддаютсястатическому анализу.12.2.1 Что представляют классы?По сути в системе бывают классы двух видов:[1]классы, которые прямо отражают понятия области приложения, т.е.
понятия, которыеиспользует конечный пользователь для описания своих задач и возможных решений; и[2]классы, которые являются продуктом самой реализации, т.е. отражают понятия,используемые разработчиками и программистами для описания способов реализации.Некоторые из классов, являющихся продуктами реализации, могут представлять и понятия реальногомира.
Например, программные и аппаратные ресурсы системы являются хорошими кандидатами нароль классов, представляющих область приложения. Это отражает тот факт, что систему можнорассматривать с нескольких точек зрения, и то, что с одной является деталью реализации, с другойможет быть понятием области приложения. Хорошо спроектированная система должна содержатьклассы, которые дают возможность рассматривать систему с логически разных точек зрения.
Приведемпример:[1]классы, представляющие пользовательские понятия (например, легковые машины игрузовики),[2]классы, представляющие обобщения пользовательских понятий (движущиеся средства),[3]классы, представляющие аппаратные ресурсы (например, класс управления памятью),[4]классы, представляющие системные ресурсы (например, выходные потоки),[5]классы, используемые для реализации других классов (например, списки, очереди,блокировщики) и[6]встроенные типы данных и структуры управления.В больших системах очень трудно сохранять логическое разделение типов различных классов иподдерживать такое разделение между различными уровнями абстракции.
В приведенном вышеперечислении представлены три уровня абстракции:[1+2] представляет пользовательское отражение системы,[3+4] представляет машину, на которой будет работать система,[5+6] представляет низкоуровневое (со стороны языка программирования) отражение реализации.Чем больше система, тем большее число уровней абстракции необходимо для ее описания, и темтруднее определять и поддерживать эти уровни абстракции. Отметим, что таким уровням абстракцииесть прямое соответствие в природе и в различных построениях человеческого интеллекта.
Например,можно рассматривать дом как объект, состоящий из[1]атомов,[2]молекул,[3]досок и кирпичей,[4]полов, потолков и стен;[5]комнат.Пока удается хранить раздельно представления этих уровней абстракции, можно поддерживатьцелостное представление о доме. Однако, если смешать их, возникнет бессмыслица. Например,предложение "Мой дом состоит из нескольких тысяч фунтов углерода, некоторых сложных полимеров,из 5000 кирпичей, двух ванных комнат и 13 потолков" - явно абсурдно. Из-за абстрактной природыпрограмм подобное утверждение о какой-либо сложной программной системе далеко не всегдавоспринимают как бессмыслицу.В процессе проектирования выделение понятий из области приложения в класс вовсе не является315Бьерн Страуструп.Язык программирования С++простой механической операцией.
Обычно эта задача требует большой проницательности. Заметим,что сами понятия области приложения являются абстракциями. Например, в природе не существуют"налогоплательщики", "монахи" или "сотрудники". Эти понятия не что иное, как метки, которымиобозначают бедную личность, чтобы классифицировать ее по отношению к некоторой системе. Частореальный или воображаемый мир (например, литература, особенно фантастика) служат источникомпонятий, которые кардинально преобразуются при переводе их в классы.
Так, экран моего компьютера(Маккинтош) совсем не походит на поверхность моего стола, хотя компьютер создавался с цельюреализовать понятие "настольный", а окна на моем дисплее имеют самое отдаленное отношение кприспособлениям для презентации чертежей в моей комнате. Я бы не вынес такого беспорядка у себяна экране.Суть моделирования реальности не в покорном следовании тому, что мы видим, а в использованииреальности как начала для проектирования, источника вдохновения и как якоря, который удерживает,когда стихия программирования грозит лишить нас способности понимания своей собственнойпрограммы.Здесь полезно предостеречь: новичкам обычно трудно "находить" классы, но вскоре этопреодолевается без каких-либо неприятностей.
Далее обычно приходит этап, когда классы и отношениянаследования между ними бесконтрольно множатся. Здесь уже возникают проблемы, связанные сосложностью, эффективностью и ясностью полученной программы. Далеко не каждую отдельную детальследует представлять отдельным классом, и далеко не каждое отношение между классами следуетпредставлять как отношение наследования. Старайтесь не забывать, что цель проекта –смоделировать систему с подходящим уровнем детализации и подходящим уровнем абстракции.
Длябольших систем найти компромисс между простотой и общностью далеко не простая задача.12.2.2 Иерархии классовРассмотрим моделирование транспортного потока в городе, цель которого достаточно точноопределить время, требующееся, чтобы аварийные движущиеся средства достигли пункта назначения.Очевидно, нам надо иметь представления легковых и грузовых машин, машин скорой помощи,всевозможных пожарных и полицейских машин, автобусов и т.п. Поскольку всякое понятие реальногомира не существует изолированно, а соединено многочисленными связями с другими понятиями,возникает такое отношение как наследование. Не разобравшись в понятиях и их взаимных связях, мыне в состоянии постичь никакое отдельное понятие.
Также и модель, если не отражает отношениямежду понятиями, не может адекватно представлять сами понятия. Итак, в нашей программе нужныклассы для представления понятий, но этого недостаточно. Нам нужны способы представленияотношений между классами. Наследование является мощным способом прямого представленияиерархических отношений. В нашем примере, мы, по всей видимости, сочли бы аварийные средстваспециальными движущимися средствами и, помимо этого, выделили бы средства, представленныелегковыми и грузовыми машинами.
Тогда иерархия классов приобрела бы такой вид:движущееся средстволегковая машинааварийное средствогрузовая машинаполицейская машинамашина скорой помощипожарная машинамашина с выдвижной лестницейЗдесь класс Emergency представляет всю информацию, необходимую для моделирования аварийныхдвижущихся средств, например: аварийная машина может нарушать некоторые правила движения, онаимеет приоритет на перекрестках, находится под контролем диспетчера и т.д.На С++ это можно задать так:classclassclassclassclassVehicle { /*...*/ };Emergency { /**/ };Car : public Vehicle { /*...*/ };Truck : public Vehicle { /*...*/ };Police_car : public Car , public Emergency {316Бьерн Страуструп.Язык программирования С++//...};class Ambulance : public Car , public Emergency {//...};class Fire_engine : public Truck , Emergency {//...};class Hook_and_ladder : public Fire_engine {//...};Наследование - это отношение самого высокого порядка, которое прямо представляется в С++ ииспользуется преимущественно на ранних этапах проектирования.
Часто возникает проблема выбора:использовать наследование для представления отношения или предпочесть ему принадлежность.Рассмотрим другое определение понятия аварийного средства: движущееся средство считаетсяаварийным, если оно несет соответствующий световой сигнал. Это позволит упростить иерархиюклассов, заменив класс Emergency на член класса Vehicle:движущееся средство (Vehicle {eptr})легковая машина (Car)грузовая машина (Truck)полицейская машина (Police_car)машина скорой помощи (Ambulance)пожарная машина (Fire_engine)машина с выдвижной лестницей (Hook_and_ladder)Теперь класс Emergency используется просто как член в тех классах, которые представляют аварийныедвижущиеся средства:classclassclassclassclassclassclassclassEmergency { /*...*/ };Vehicle { public: Emergency* eptr; /*...*/ };Car : public Vehicle { /*...*/ };Truck : public Vehicle { /*...*/ };Police_car : public Car { /*...*/ };Ambulance : public Car { /*...*/ };Fire_engine : public Truck { /*...*/ };Hook_and_ladder : public Fire_engine { /*...*/ };Здесь движущееся средство считается аварийным, если Vehicle::eptr не равно нулю.
"Простые"легковые и грузовые машины инициализируются Vehicle::eptr равным нулю, а для других Vehicle::eptrдолжно быть установлено в ненулевое значение, например:Car::Car(){eptr = 0;}Police_car::Police_car(){eptr = new Emergency;}// конструктор Car// конструктор Police_carТакие определения упрощают преобразование аварийного средства в обычное и наоборот:void f(Vehicle* p){delete p->eptr;p->eptr = 0;// больше нет аварийного движущегося средства//...p->eptr = new Emergency;// оно появилось снова}Так какой же вариант иерархии классов лучше? В общем случае ответ такой: "Лучшей является317Бьерн Страуструп.Язык программирования С++программа, которая наиболее непосредственно отражает реальный мир". Иными словами, при выборемодели мы должны стремиться к большей ее"реальности", но с учетом неизбежных ограничений,накладываемых требованиями простоты и эффективности.