Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


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



• Для постфиксных унарных операций в функцию-оператор последним аргументом передается фиктивный параметр типа int

• Для встроенных операций их интерпретация определяется как комбинация других операций

• Для пользовательских типов ответственность за соблюдение таких правил целиком лежит на программисте

 

• Перегрузка оператора может помочь писать интуитивно понятный и «красивый» код.

• При перегрузке операторов нужно «играть по правилам». Некоторые из них проверит компилятор, за некоторыми нужно следить самостоятельно

• Не злоупотребляйте переопределением операторов, смысл операторов должен быть понятен другим программистам

Косвенное обращение

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


Очевидно, реализация этого способа требует определения указателя на функцию. Делается это достаточно просто, путем объявления переменной соответствующего типа:

 

int (*funcptr)(int, int);

 

Этот пример объявляет переменную funcptr типа указатель на функцию, возвращающую результат типа int и имеющую 2 параметра типа int. При объявлении указателя на функцию запись выглядит слегка шиворот-навыворот, но так уж требует Си: сначала мы указываем тип результата функции, затем обязательно в круглых скобках идентификатор (не забудьте про звездочку - признак указателя), а затем, опять в круглых скобках, список типов параметров функции.


Сделанная запись - не что иное, как объявление переменной с новым типом. Можно определить этот тип при помощи typedef, после чего определять указатели на функции станет легче:

 

typedef int (*t_func)(int, int);

t_func funcptr;


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

 

funcptr = (t_func)0x1000;

funcptr(3, 12); // вызов функции с параметрами по абсолютному адресу


В этом примере можно было бы использовать и такое присваивание значения указателю:


funcptr = (void *)0x1000;

 

так как тип указателя (void *) совместим с любым иным типом указателя.

Индексирование

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

Оператор [] может быть перегружен, чтобы получать единственный аргумент произвольного типа и

возвращать произвольный тип в качестве своего значения.

Поскольку оператор [] может вызываться лишь с одним аргументом, для имитации многомерных

массивов часто применяют анонимные экземпляры.

Хотя оператор [] вызывается лишь с одним аргументом, этот умеренно неуклюжий синтаксис

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

Вызов функции new и delete.

В языке программирования C++ оператор delete возвращает память, выделенную оператором new, обратно в кучу. Вызов delete должен происходить для каждого вызова new, чтобы избежать утечки памяти. После вызова delete объект, указывающий на этот участок памяти, становится некорректным и не должен больше использоваться. Многие программисты присваивают 0 (нуль-указатель) указателям после использования delete, чтобы минимизировать количество ошибок программирования. Однако нужно отметить, что удаление нуль-указателя фактически не имеет эффекта, так что нет необходимости проверять нуль-указатель перед вызовом delete.

В языке программирования C++, new — оператор, обеспечивающий выделение динамической памяти в куче. За исключением формы, называемой «размещающей формой new», new пытается выделить достаточно памяти в куче для размещения новых данных и, в случае успеха, возвращает адрес свежевыделенной памяти. Однако, если new не может выделить память в куче, то он передаст (throw) исключение типа std:: bad_alloc. Это устраняет необходимость явной проверки результата выделения.

Для освобождения памяти используется операция (операторная функция) delete. Подобно операторной функции new, delete также является статическим членом класса.

В контексте выражений размещения и удаления могут быть использованы стандартные операции C++ new и delete, а может быть обеспечен вызов операторных функций operator new и operator delete.

Согласно грамматике C++, основным операндом для символа операции new в выражении размещения является заключённое в круглые скобки ИмяТипа, либо ИмяТипаNew (без скобок), которое разворачивается в конструкцию, содержащую информацию о размерах размещаемого массива

Билет №17. Шаблоны

На сцену выходит механизм шаблонов — усовершенствованный макропроцессор для директив

#define. Шаблоны представляют собой ничто иное, как макросы без всех перечисленных

ограничений. Они могут быть вложенными. Вам не придется беспокоиться о дублировании их

функций. Большинство отладчиков C++ при возникновении ошибки правильно указывает строку

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

прекрасную программу закорючками вроде \ и ##.

Если вы собираетесь использовать шаблоны, привыкайте к тому, что в вашей речи будет часто звучать

термин параметризованный (parameterized). Шаблоны используются для создания параметризованных

типов (обычно классов) и параметризованных функций.

Параметризованные типы

Параметризованный тип внешне представляет собой обычное объявление класса, которому

предшествует магическое заклинание template < c1ass Type>, где Type — выбранное вами

символическое имя (остальные элементы задаются жестко). Всюду, где символическое имя Type (или

другое имя) встречается в объявлении класса оно интерпретируется как макрос, вместо которого при

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

Параметризованные функции

Параметризованные функции объявляются точно так же — перед их объявлениями указывается

формула template.... Синтаксис шаблона должен повторяться как при объявлении, так и при

определении функции. Помните, шаблоны на самом деле являются макросами, поэтому они должны

находиться в файлах.h. Если определение будет находиться в файле.срр, программа работать не будет

(если только это не единственный файл.срр, в котором вызывается данная функция).

Передача параметра

Многочисленные символы < и > вызывают изрядную путаницу, поскольку C++ не всегда

последователен. Вообще говоря, < Туре> следует указывать везде, кроме трех мест в объявлениях

классов или определениях их функций:

1. За ключевым словом class в самом начале.

2. При указании имени конструктора.

3. При указании имени деструктора.

Аргументы конструкторов и деструкторов должны быть параметризованными, как и все использования

имени класса за исключением трех указанных случаев. При любом использовании параметризованного типа или функции необходимо указывать параметр. Было бы намного проще, если бы C++ просто

требовал присутствия параметра во всех случаях, но это же C++... Вдобавок можно сэкономить

несколько символов в исходном тексте программы. В трех указанных ситуациях компилятор может

сделать разумные предположения по поводу отсутствующих символов.

Шаблоны с несколькими параметрами

Тип может иметь более одного параметра, хотя такие ситуации встречаются довольно редко. Увидев

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

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

аналогично, разве что вместо < c1ass Type> используется список типа < c1ass Type1, class

Type2>. Наконец, параметр не обязан быть классом. Он может быть структурой или еще чем-нибудь,

хотя именно классы прочно удерживают ведущие позиции на рынке параметров.

Долой вложенные параметризованные типы!

Увы, такая возможность существует, но пожалуйста, пользуйтесь ею с максимальной осторожностью.

Вложенные шаблоны не только плохо читаются, но и генерируют огромное количество кода при

расширении. Помните, что при использовании шаблона самого верхнего уровня будут расширены все

шаблоны.

 

Последний пункт очень важен. Речь идет о неявных требованиях к типу – параметру шаблона

Эти требования выясняются только при конкретизации класса.

Например, на слайде 31 я забыл убрать параметр по умолчанию 0 для параметра конструктора класса Link

Это накладывает ограничение на тип, который можно использовать в качестве контейнера:

он должен обязательно приводиться к целому числу.

 

Если такое приведение невозможно, конкретный класс не будет скомпилирован.

 

Фабрика-шаблон

 

Фабрика-шаблон (С++)

Команды чтения из файла и создания теперь не зависят от конкретики; это значит, что Мы можем добавлять новые овощи сколько душе угодно без вмешательства в код взаимодействия с пользователем. Более того, новые овощи можно добавлять «на лету», то есть в идеале даже без перекомпиляции программы

Плохая новость – на каждый овощ нужна собственная фабрика.
Хорошая новость – у нас есть шаблонная реализация, так что много кода писать не надо.

Побочные эффекты
(требует дальнейшей доработки)

· Application теперь отвечает и за создание объектов, кроме своих прямых обязанностей – взаимодействия с пользователем, чем нарушает принцип???

· Интерфейс «создатель» может создавать только овощи, и ничего больше, чем нарушает принцип!!!

(Если захочется создавать фрукты, потребуется другая иерархия классов-создателей)

??? - единственности ответственности

!!! - инвертирования зависимостей

 


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-07-14; Просмотров: 534; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.035 с.)
Главная | Случайная страница | Обратная связь