Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Stoneshard |#11| Battle at the castle
Stoneshard |#10| A busy reaper
The Elder Scrolls IV: Oblivion Remastered - Trash review
Stoneshard |#9| A Million Liches

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Страустрап Б. Весь текст 579.17 Kb

Язык С++

Предыдущая страница Следующая страница
1 ... 24 25 26 27 28 29 30  31 32 33 34 35 36 37 ... 50
     всех допустимых выражений.
  8. (*2) Определите  класс INT,  который ведет себя в точности как
     int. Подсказка: определите INT::operator int().
  9. (*1) Определите  класс RINT, который ведет себя в точности как
     int за исключением того, что единственные возможные операции -

                             - стр 197 -

     это +  (унарный и  бинарный), - (унарный и бинарный), *, /, %.
     Подсказка: не определяйте $ (R?)INT::operator int().
  10. (*3)  Определите   класс  LINT,  ведущий  себя  как  RINT  за
     исключением того, что имеет точность не менее 64 бит.
  11. (*4)  Определите   класс,  который   реализует  арифметику  с
     произвольной точностью.  Подсказка: вам надо управлять памятью
     аналогично тому, как это делалось для класса string.
  12. (*2) Напишите  программу, доведенную до нечитаемого состояния
     с помощью макросов и перегрузки операций. Вот идея: определите
     для INT  + чтобы  он означал  - и  наоборот, а потом с помощью
     макроопределения определите int как INT. Переопределение часто
     употребляемых  функций,  использование  параметров  ссылочного
     типа и  несколько вводящих  в заблуждение комментариев помогут
     устроить полную неразбериху.
  13. (*3) Поменяйтесь  со своим  другом программами, которые у вас
     получились в предыдущем упражнении. Не запуская ее попытайтесь
     понять, что  делает программа  вашего друга.  После выполнения
     этого упражнения вы будете знать, чего следует избегать.
  14. (*2) Перепишите  примеры с  comlpex (#6.3.1), tiny (#6.3.2) и
     string (#6.9)  не используя friend функций. Используйте только
     функции члены.  Протестируйте каждую из новых версий. Сравните
     их с  версиями, в которых используются функции друзья. Еще раз
     посмотрите Упражнение 5.3.
  15. (*2)  Определите  тип  vec4  как  вектор  их  четырех  float.
     Определите operator[]  для vec4.  Определите операции +, -, *,
     /, =,  +=, -=,  *=,  /=  для  сочетаний  векторов  и  чисел  с
     плавающей точкой.
  16. (*3) Определите  класс  mat4  как  вектор  из  четырех  vec4.
     Определите для  mat4 operator[], возвращающий vec4. Определите
     для этого  типа обычные  операции  над  матрицами.  Определите
     функцию, которая производит исключение Гаусса для mat4.
  17. (*2) Определите  класс vector, аналогичный vec4, но с длиной,
     которая      задается      как      параметр      конструктора
     vector::vector(int).
  18. (*3)  Определите   класс  matrix,   аналогичный  mat4,  но  с
     размерностью,      задаемой      параметрами      конструктора
     matrix::matrix(int,int).


                           Глава 7

                        Производные Классы

                       Не надо размножать объекты без необходимости
                                                         - У. Оккам

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

     7.1 Введение

  Представим себе  процесс  написания  некоторого  средства  общего
назначения  (например,  тип  связанный  список,  таблица  имен  или
планировщик для  системы моделирования),  кооторое  предназначается
для   использоания    многими   разными    людьми    в    различных
обстоятельствах. Очевидно,  что в  кандидатах на роль таких средств
недостатка нет,  и выгоды  от их  стандартизации огромны.  Кажется,
любой опытный  программист написал  (и  отладил)  дюжину  вариантов
типов множества,  таблицы имен,  сортирующей  функции  и  т.п.,  но
оказывается, что  таблиц имен каждый программист и каждая программа
используют свою  версию этих  понятий, из-за чего программы слишком
трудно читать,  тяжело отлаживать  и сложно  модифицировать.  Более
того,  в  большой  программе  вполне  может  быть  несколько  копий
идентичных (почти) частей кода для работы с такими фундаментальными
понятиями.
  Причина этого хаоса частично состоит в том, что представить такие
общие понятия в языке программирования сложно с концептульной точки
зрения, а  частично в  том, что  средства,  обладающие  достаточной
общностью, налагают  дополнительные  расходы  по  памяти  и/или  по
времени, что  делает их  неудобными для  самых простых  и  наиболее
напряженно используемых средств (связанные списки, вектора и т.п.),
где они  были бы  наиболее полезны.  Понятие производного  класса в
C++, описываемое  в #7.2,  не обеспечивают общего решения всех этих
проблем, но оно дает способ справляться с довольно небольшим числом
важных  случаев.   Будет,  например,   показано,   как   определить
эффективный класс общего связанного списка таким образом, чтобы все
его версии использовали код совместно.
  Написание  общецелевых   средств  -  задача  непростая,  и  часто
основной акцент в их разработке другой, чем при разработке программ
специального  назначения.   Конечно,  нет   четкой  границы   между
средствами общего и специального назначения, и к методам и языковым
                             - стр 199 -

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

     7.2 Производные Классы

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

     7.2.1 Построение Производного Класса

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

  struct employee {          // служащий
      char*    name;        // имя
      short    age;         // возраст
      short    department;  // подразделение
      int      salary;      //
      employee* next;
      // ...
  };

Список аналогичных  служащих  будет  свзяваться  через  поле  next.
Теперь давайте определим менеджера:

  struct manager {           // менеджер
      employee emp;          // запись о менеджере как о служащем
      employee* group;       // подчиненные люди
      // ...
  };

Менеджер также  является служащим; относящиеся к служащему employee
данные хранятся  в члене  emp объекта  manager. Для  читающего  это
человека это,  может быть, очевидно, но нет ничего выделяющего член
emp для  компилятора. Указатель на менеджера (manager*) не является
указателем на  служащего (employee*),  поэтому просто  использовать
один  там,  где  требуется  другой,  нельзя.  В  частности,  нельзя
поместить  менеджера  в  список  служащих,  не  написав  для  этого
специальную  программу.  Можно  либо  применить  к  manager*  явное
преобразование типа,  либо поместить  в список служащих адрес члена
emp, но  и то и другое мало элегантно и довольно неясно. Корректный
подход состоит  в том,  чтобы  установить,  что  менеджер  является
служащим с некоторой добавочной информацией:

                             - стр 200 -

  struct manager : employee {
      employee* group;
      // ...
  };

manager является  производным от employee и, обратно, employee есть
базовый класс  для manager.  Класс manager  дополнительно  к  члену
group имеет члены класса employee (name, age и т.д.).
  Имея определения  employee и  manager  мы  можем  теперь  создать
список  служащих,   некоторые  из   которых  являются  менеджерами.
Например:

  void f()
  {
      manager m1, m2;
      employee e1, e2;
      employee* elist;
      elist = &m1;     // поместить m1, e1, m2 и e2 в elist
      m1.next = &e1;
      e1.next = &m2;
      m2.next = &e2;
      e2.next = 0;
  }

Поскольку менеджер является служащим, manager* может использоваться
как employee*.  Однако служащий  необязательно является менеджером,
поэтому использовать employee* как manager* нельзя.

     7.2.2 Функции Члены

  Просто структуры данных вроде employee и manager на самом деле не
столь интересны  и часто  не особенно  полезны, поэтому рассмотрим,
как добавить к ним функции. Например:

  class employee {
      char* name;
      // ...
  public:
      employee* next;
      void print();
      // ...
  };

  class manager : public employee {
      // ...
  public:
      void print();
      // ...
  };

Надо  ответить   на  некоторые  вопросы.  Как  может  функция  член
производного класса  manager использовать члены его базового класса
employee? Как  члены базового  класса employee  могут  использовать
функции члены  производного класса  manager? Какие  члены  базового
класса employee  может использовать функция не член на объекте типа

                             - стр 201 -

manager? Каким  образом программист может повлиять на ответы на эти
вопросы, чтобы удовлетворить требованиям приложения?
  Рассмотрим:

  void manager::print()
  {
      cout << " имя " << name << "\n";
      // ...
  }

Член производного  класса может использовать открытое имя из своего
базового  класса   так  же,  как  это  могут  делать  другие  члены
последнего, то  есть без  указания объекта.  Предполагается, что на
объект указывает  this, поэтому  (корректной) ссылкой  на имя  name
является this->name.  Однако функция manager::print компилироваться
не будет,  член производного класса не имеет никакого особого права
доступа к закрытым членам его базового класса, поэтому для нее name
недоступно.
  Это многим  покажется удивительным,  но представьте  себе  другой
вариант: что  функция член  могла бы  обращаться к  закрытым членам
своего  базового   класса.  Возможность,  позволяющая  программисту
получать доступ  к закрытой части класса просто с помощью вывода из
него другого  класса, лишила  бы понятие  закрытого  члена  всякого
смысла.  Более  того,  нельзя  было  бы  узнать  все  использования
закрытого имени  посмотрев на функции, описанные как члены и друзья
этого класса.  Пришлось бы  проверять каждый  исходный файл во всей
программе на  наличие в  нем производных классов, потом исследовать
каждую функцию  этих классов,  потом искать все классы, производные
от этих  классов, и  т.д. Это  по меньшей мере утомительно и скорее
всего нереально.
  С другой  стороны, можно ведь использовать механизм friend, чтобы
предоставить такой доступ или отдельным функциям, или всем функциям
отдельного класса (как описывается в #5.3). Например:

  class employee {
      friend void manager::print();
      // ...
  };

решило бы проблему с manager::print(), и

  class employee {
  friend class manager;
      // ...
  };

сделало бы  доступным каждый  член employee для всех функций класса
manager.   В    частности,   это   сделает   name   доступным   для
manager::print().
  Другое, иногда более прозрачное решение для производного класса,-
использовать только открытые члены его базового класса. Например:
Предыдущая страница Следующая страница
1 ... 24 25 26 27 28 29 30  31 32 33 34 35 36 37 ... 50
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 
Комментарии (4)

Реклама