Еще один пример перегрузки операторов



 

Завершая тему перегрузки операторов, рассмотрим пример, который часто называют квинтэссенцией примеров, посвященных перегрузке операторов, а именно класс строк. Несмотря на то что С++‑подход к строкам (которые реализуются в виде символьных массивов с завершающим нулем, а не в качестве отдельного типа) весьма эффективен и гибок, начинающие С++‑программисты часто испытывают недостаток в понятийной ясности реализации строк, которая присутствует в таких языках, как BASIC. Конечно же, эту ситуацию нетрудно изменить, поскольку в C++ существует возможность определить класс строк, который будет обеспечивать реализацию строк подобно тому, как это сделано в других языках программирования. По правде говоря, "на заре" развития C++ реализация класса строк была забавой для программистов. И хотя стандарт C++ теперь определяет строковый класс, который описан ниже в этой книге, вам будет полезно реализовать простой вариант такого класса самим. Это упражнение наглядно иллюстрирует мощь механизма перегрузки операторов.

Сначала определим "классовый" тип str_type .

 

 

Как видите, в классе str_type объявляется закрытый символьный массив string , предназначенный для хранения строки. В данном примере условимся, что размер строк не будет превышать 79 байт . В реальном же классе строк память для их хранения должна выделяться динамически, и это ограничение действовать не будет. Кроме того, чтобы не загромождать логику этого примера, мы решили освободить этот класс (и его функции‑члены) от контроля выхода за границы массива. Безусловно, в любой настоящей реализации подобного класса должен быть обеспечен полный контроль за ошибками.

Этот класс имеет один конструктор, который можно использовать для инициализации массива string с использованием заданного значения или для присваивания ему пустой строки в случае отсутствия инициализатора. В этом классе также объявляются два перегруженных оператора, которые выполняют конкатенацию и присваивание. Наконец, класс str_type содержит функцию show_str() , которая выводит строку на экран. Вот как выглядит код операторных функций operator+() и operator=() .

 

 

Имея определения этих функций, покажем, как их можно использовать на примере следующей функции main().

 

 

При выполнении эта программа выводит на экран строку Всем привет . Сначала она конкатенирует строки (объекты класса str_type ) а и b , а затем присваивает результат конкатенации строке c .

Следует иметь в виду, что операторы "=" и "+" определены только для объектов типа str_type . Например, следующая инструкция неработоспособна, поскольку она представляет собой попытку присвоить объекту а строку с завершающим нулем.

 

 

Но класс str_type, как будет показано ниже, можно усовершенствовать и разрешить выполнение таких инструкций.

Для расширения круга операций, поддерживаемых классом str_type (например, чтобы можно было объектам типа str_type присваивать строки с завершающим нулем или конкатенировать строку с завершающим нулем с объектом типа str_type ), необходимо перегрузить операторы "=" и "+" еще раз. Вначале изменим объявление класса.

 

 

Затем реализуем перегрузку операторных функций operator+() и operator=() .

 

 

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

 

 

Следующая программа включает дополнительные определения операторов "=" и "+" .

 

 

При выполнении эта программа отображает на экране следующее.

 

 

Прежде чем переходить к следующей главе, убедитесь в том, что до конца понимаете, как получены эти результаты. Теперь вы можете сами определять другие операции над строками. Попытайтесь, например, определить операцию удаления подстроки на основе оператора "‑" . Так, если строка объекта А содержит фразу "Это трудный‑трудный тест" , а строка объекта В – фразу "трудный" , то вычисление выражения А‑В даст в результате "Это ‑ тест" . В данном случае из исходной строки были удалены все вхождения подстроки "трудный" . Определите также "дружественную" функцию, которая бы позволяла строке с завершающим нулем находиться слева от оператора "+" . Наконец, добавьте в программу код, обеспечивающий контроль за ошибками.

Важно! Для создаваемых вами классов всегда имеет смысл экспериментировать с перегрузкой операторов. Как показывают примеры этой главы, механизм перегрузки операторов можно использовать для добавления новых типов данных в среду программирования. Это одно из самых мощных средств C++.

 

Глава 14: Наследование

 

Наследование – один из трех фундаментальных принципов объектно‑ориентированного программирования, поскольку именно благодаря ему возможно создание иерархических классификаций. Используя наследование, можно создать общий класс, который определяет характеристики, присущие множеству связанных элементов. Этот класс затем может быть унаследован другими, узкоспециализированными классами с добавлением в каждый из них своих, уникальных особенностей.

В стандартной терминологии языка C++ класс, который наследуется, называется базовым. Класс, который наследует базовый класс, называется производным. Производный класс можно использовать в качестве базового для другого производного класса. Таким путем и строится многоуровневая иерархия классов.

 

Понятие о наследовании

 

Базовый класс наследуется производным классом.

Язык C++ поддерживает механизм наследования, позволяя в объявление класса встраивать другой класс. Для этого базовый класс задается при объявлении производного. Лучше всего начать с примера. Рассмотрим класс road_vehicle , который в самых общих чертах определяет дорожное транспортное средство. Его члены данных позволяют хранить количество колес и число пассажиров, которое может перевозить транспортное средство.

 

 

Это общее определение дорожного транспортного средства можно использовать для определения конкретных типов транспортных средств. Например, в следующем фрагменте путем наследования класса road_vehicle создается класс truck (грузовых автомобилей).

 

 

Тот факт, что класс truck наследует класс road_vehicle , означает, что класс truck наследует все содержимое класса road_vehicle . К содержимому класса road_vehicle класс truck добавляет член данных cargo , а также функции‑члены, необходимые для поддержки члена cargo .

Обратите внимание на то, как наследуется класс road_vehicle . Общий формат для обеспечения наследования имеет следующий вид.

 

 

Здесь элемент доступ необязателен. При необходимости он может быть выражен одним из спецификаторов доступа: public , private или protected . Подробнее об этих спецификаторах доступа вы узнаете ниже в этой главе. А пока в определениях всех наследуемых классов мы будем использовать спецификатор public . Это означает, что все public ‑члены базового класса также будут public ‑членами производного класса. Следовательно, в предыдущем примере члены класса truck имеют доступ к открытым функциям‑членам класса road_vehicle , как будто они (эти функции) были объявлены в теле класса truck . Однако класс truck не имеет доступа к private ‑членам класса road_vehicle . Например, для класса truck закрыт доступ к члену данных wheels .

Рассмотрим программу, которая использует механизм наследования для создания двух подклассов класса road_vehicle : truck и automobile .

 

 

При выполнении эта программа генерирует такие результаты.

 

 

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

И еще. Обратите внимание на то, что оба класса truck и automobile включают функцию‑член show() , которая отображает информацию об объекте. Эта функция демонстрирует еще один аспект объектно‑ориентированного программирования – полиморфизм. Поскольку каждая функция show() связана с собственным классом, компилятор может легко "понять", какую именно функцию нужно вызвать для данного объекта.

После ознакомления с общей процедурой наследования одним классом другого можно перейти и к деталям этой темы.

 


Дата добавления: 2018-09-22; просмотров: 298; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!