Главная » Программирование звука » Ввод/вывод C++

0

Перегрузка операторов

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

C++ позволяет переопределять базовые операторы. B действительности выражение, подобное а+b, преобразуется в а.operator+ (b) (или в operator+(a, b), в зависимости от контекста). Определяя (или переопределяя) функцию-член

operator+, вы способны изменять способ сложения. (Однако не можете затраги-

вать существующие операторы или добавлять новые.)

Данное  преимущество  небезопасно,  и  вам  не  следует  часто  им  пользоваться. Легко   создать   неудобочитаемые   программы,   переопределяя   базовые   операторы. Я  учитываю  это  при  программировании;  а  здесь  рассказываю  о  нем  потому,  что стандартная библиотека C++ переопределяет некоторые операторы.

Ввод/вывод

Стандартная  библиотека  C++  переопределяет  операторы  <<  и  >>.  При  соседстве с числами они все еще означают «сдвиг влево» и «сдвиг вправо». Однако если левый операнд является потоковым классом (stream class), они реализуют ввод/вывод. Например, запись cout << "Hello\n" посылает строку "Hello\n" в поток cout. (cout это поток C++, эквивалентный декриптору файла stdout в C). Заметим, что данный механизм является типонезависимым (type-safe), компилятор выбирает нужное определение operator<< в зависимости от типов. B противоположность этому, оператор printf языка C ожидает, что вы предоставите спецификатор формата, соответствующий используемым вами типам.

Существует  четыре стандартных  потока ввода/вывода, имена  которых   cout, cin, cerr и  clog.  Первые  три  соответствуют  дескрипторам  stdout, stdin и  stderr языка  C.  Ввод/вывод  C++  удобно  использовать,  если  форматирование выходной  информации  неважно.  Вы  даже  можете  применять  несколько  операторов << и >> вполне очевидным способом:

cout << "Переменная  i имеет значение: " << i << "\n";

Однако  я  предпочитаю  в  большинстве  случаев  для  форматирования  выход-

ной информации использовать printf, характерный для C.

Дополнения

Преобразование типов в C++

Преобразования    типов    достаточно    распространены    в    программировании на C. Хотя и не так часто, они все еще нужны в C++. Стандартная запись C вида (int) var, к сожалению, не  слишком  ясна,  так как она не  определяет  причины приведения.

Точно  определяя  цель  приведения,  компилятор  может  помочь  вам  проверить код.  Для  облегчения  обнаружения  ошибок  и  более  легкого  ориентирования  в  программном  коде  C++  предоставляет  новую  форму  записи,  которая  определяет  цель приведения типов:

?  reinterpret_cast<тип> (выражение). При использовании этой  записи  те  же  биты  данных  трактуются  как  представители  нового  типа.   Она применяется,  в  основном,  для  приведения  типа  указателей,  например,  так: reinterpret_cast<int *>(buffer);

?  static_cast<тип> (выражение). Эта запись заставляет компилятор осу-

ществить   стандартное   преобразование,   такое   как   static_cast<int>

(sin(x));

?  const_cast<тип> (выражение). Подобная запись изменяет принадлеж-

ность  выражения  к  типам  const или  volatile.  Заметим,  что  это  един-

ственная   новая   возможность   приведения,   которая  может   использоваться

в подобных целях;

?  dynamic_cast<тип> (выражение). Данная запись преобразует указатель

на объект в указатель на совместимый объект. Корректность этого преобра-

зования динамически проверяется во время выполнения. Это наименее рас-

пространенная из новых форм записей приведения.

Ссылки

C++  создает  альтернативу  указателям  C   во  многих  случаях  более  безопасную и простую. Объявление, подобное int &i, делает i ссылкой (reference) на целое  значение.  Внутренне  ссылка  обрабатывается  так  же,  как  и  указатель.  Приносит выгоду передача больших объектов в функцию с использованием ссылок.

Тем не менее ссылки это не указатели. Ссылка использует отличный от указателей  синтаксис.  Вы  применяете  для  ссылки  на  элемент  класса  или  структуры оператор «точка» (. ), а не оператор «стрелка» (->). Ссылочная переменная должна  быть  проинициализирована  при  своем  создании.  Невозможна  ссылка,  содержащая значение NULL, равно как и невозможно изменить значение ссылки.

Заметим,  что  использование  оператора  &  для  объявления  ссылки  не  вступа-

ет в противоречие с использованием & в качестве оператора взятия адреса.

Я  редко  использую  ссылки,  но  они  необходимы  для  определенных  системных классов.

Оператор this

Когда  вы  вызываете  функцию-член  через  запись  object.Foo(a, b),  компилятор  преобразует  ее  в  запись  Foo(&object, a, b).  Аналогично,  если  вы определяете  функцию-член  Foo() в  классе,  на  самом  деле  используется  запись Foo (class *this). Каждая функция-член (за исключением методов класса, описанных со спецификатором static, которые я не собираюсь рассматривать) имеет специальную  переменную  с  именем  this  указатель  на  текущий  объект.  Внутри функции-члена  вы  можете  вызывать  другие  функции-члены  либо  в  форме  Bar(), либо в форме this->Bar(). Оператор this редко используется явно. Однако он полезен,  когда  какому-либо  объекту  необходимо  передать  другому  объекту  указатель на себя. (Как в SampledInstrument::NewNote, глава 21. Объект SampledInstrument передает  SampledNote указатель  на  себя  для  того,  чтобы  нотный объект получил доступ к общим данным, хранимым в инструментном объекте.)

Друзья

Соответствующее  использование  спецификаторов  private и  public может значительно  упростить  жизнь.  Накладывая  ограничения  на  эксплуатацию  вашего класса другими людьми, вы можете упростить будущее изменение реализации. Однако  существует  одно  исключение.  Предположим,  у  вас  есть  два  сильно  связанных класса, причем каждый из них должен иметь доступ к приватным данным

другого, или специальная функция, требующая доступа к данным класса, кото-

рые, в свою очередь, необходимо сделать приватными.

Определяя  функцию  или  класс  как  дружественную  (спецификатор  friend), вы   заставляете   компилятор   специально   разрешить   функции   или   классу   доступ к  приватным  данным.  Например,  в  классе  главы  6  мне  понадобилось  создать  функцию, которая не будучи членом класса (чтобы я мог передать указатель на эту функцию  системным  аудиоутилитам),  имела  бы  доступ  к  приватным  данным  класса. Аналогично  в  главе 21 мне необходимо  было  сделать так, чтобы  объект SampledNote мог  обращаться  к  разделяемой  нотной  информации,  хранимой  в  ассоциированном объекте SampledInstrument.

Источник: Кинтцель Т.  Руководство программиста по работе со звуком = A Programmer’s Guide to Sound: Пер. с англ. М.: ДМК Пресс, 2000. 432 с, ил. (Серия «Для программистов»).

По теме:

  • Комментарии