- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
- •Оглавление
- •Предисловие
- •Введение
- •1.1. Понятие класса и объекта. Инкапсуляция
- •1.2. Определение классов. Компоненты. Доступность
- •Class_key /*class_id*/ { /*members_list*/ };
- •Value_type class_id::function_id(parameters) {statements}
- •CPoint point1(100,70); // локальный объект
- •Static cPoint point3(50,120); // статический объект
- •Class_id(parameters) /*:initializer_list*/ {/*statements*/}
- •CString(const char *);
- •Delete[] __thematrix;
- •1.4. Обращение к компонентам объектов
- •1.5. Статические и нестатические компоненты классов
- •1.7. Указатель this
- •В опросы для самопроверки
- •2. Механизм наследования. Полиморфизм
- •2.1. Формы наследования. Базовые и производные классы
- •Class_key class_id: inheritance_specifier base_class_id {member_list};
- •2.3. Абстрактные классы
- •2.4. Множественное наследование и виртуальные классы
- •2.5. Преобразование динамических типов. Динамическая идентификация типов
- •Catch ( std::bad_cast & ) { // обработка исключения
- •Return 0;
- •Вопросы для самопроверки
- •3. Дружественные функции и классы
- •3.1. Дружественные функции
- •3.2. Дружественные классы
- •Вопросы для самопроверки
- •4. Механизм вложения
- •4.1. Вложенные классы
- •4.2. Локальные классы
- •Вопросы для самопроверки
- •5. Объектная модель и шаблоны
- •5.1. Определение, описание и инстанцирование шаблонов
- •::Function_id(function_parameter_list) { statements }
- •5.2. Параметры и аргументы шаблонов
- •Class identifier typename identifier
- •// Key, Data – параметры-типы (типы ключа и данных отображения)
- •// Container – контейнер, где содержится информация отображения class сMap {
- •Class MyTemplate
- •Int array[10]; struct Structure { int m; static int sm; } str;
- •5.3. Шаблоны компонентных функций
- •Value_type function_template_id(function_parameter_list) { statements }
- •::Function_template_id(function_parameter_list) { statements }
- •5.4. Специализация шаблонов
- •Вопросы для самопроверки
- •6. Перегрузка операций
- •Value_type operator @ (parameter_list);
- •Value_type operator @ (parameter_list) { statements }
- •Return fail();
- •6.3. Перегрузка бинарных операций
- •Value_type operator @ (parameter); // компонентная функция
- •Value_type operator @ (parameter, parameter); // глобальная функция friend value_type operator @ (parameter, parameter); // дружественная функция
- •Return *this;
- •Return *this;
- •/* Присваиваем собственные данные класса d */
- •6.4. Перегрузка операций управления памятью
- •Typedef void (*new_handler) ();
- •Extern new_handler set_new_handler( new_handler new_p );
- •Void operator delete(void * memory) {
- •... // Специальная обработка пользователя ::operator delete(memory); // освободить память
- •Вопросы для самопроверки
- •7. Механизм исключений
- •Throw expression
- •7.3. Специальные средства поддержки механизма исключений
- •Unexpected_function set_unexpected(unexpected_function func_name);
- •Typedef void (* unexpected_function) ();
- •Extern char * __throwExceptionName; extern char * __throwFileName; extern unsigned __throwLineNumber;
- •Вопросы для самопроверки
- •8. Подсчет ссылок
- •8.1. Назначение механизма подсчета ссылок
- •8.2. Контекстно-независимая модель счетчика ссылок
- •8.4. Внедрение подсчета ссылок в существующий класс
- •Вопросы для самопроверки
- •9. Стандартная библиотека шаблонов (stl)
- •9.1. Назначение и архитектура stl
- •9.2. Последовательные контейнеры
- •Class vector {
- •// Определение итераторов
- •Sort(first,last); // сортировка вектора в диапазоне итераторов
- •Ifstream ifile ("example.In"); ofstream ofile ("example.Out");
- •OutputIterator copy(
- •InputIterator first, InputIterator last, OutputIterator result );
- •// Заполнение списка
- •Operator- (int)
- •Operator- (random access iterator) operator[] (int)
- •InputIterator find(InputIterator first, InputIterator last, const t & value);
- •InputIterator find(InputIterator first, InputIterator last, const t & value)
- •Return first;
- •OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
- •Return result;
- •OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op)
- •Return result;
- •Void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp)
- •__Quick_sort_loop(first, last, comp); __final_insertion_sort(first, last, comp);
- •T accumulate(InputIterator first, InputIterator last, t init, Function f);
- •V.Push_back(2); V.Push_back(5);
- •9.5. Функторы
- •T operator()(const t & X) const { return -X; }
- •9.7. Адаптеры
- •S1.Push(1); s1.Push(5);
- •// Записать в вектор числа 1 2 3 4
- •// Сортировать по неубыванию
- •// Записать в вектор числа 4 6 10 3 13 2
- •Вопросы для самопроверки
- •Заключение
- •Библиографический Список
- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
6. Перегрузка операций
Объектная модель С++ дает широкие возможности программисту в приспособлении средств языка к новым потребностям. Одной из таких возможностей является перегрузка (overloading) стандартных операций. Если при разработке приложения оказывается, что некоторая операция не применима к операндам пользовательского типа (класса) или ее поведение отличается от требуемого, то ее действие можно расширить (скорректировать) на такие операнды. Расширение действия сводится к определению для операции нового алгоритма, который будет использоваться при наличии операндов пользовательского класса. Это и составляет сущность перегрузки операций в С++.
6.1. Назначение перегрузки операций и ее реализация в С++
Необходимость перегрузки операций возникает весьма часто. Пусть, например, в приложении определен класс матриц (см. задачи предыдущих глав) и необходимо сделать так, чтобы отдельные матрицы можно было перемножать, используя традиционный синтаксис A*B, характерный для встроенных типов. Для этого достаточно лишь расширить действие бинарной операции * на класс матриц путем ее перегрузки. В результате выражения типа A*B, где оба операнда – матрицы станут обрабатываться по алгоритму, предусмотренному перегрузкой24.
Для перегрузки некоторой стандартной операции, обозначаемой символом @25, необходимо определить соответствующую функцию-операцию (operator function). Эта функция-операция будет содержать запись нового алгоритма операции @; он будет применяться при обработке операндов (или операнда) пользовательского класса. Для того, чтобы обеспечить связь функции-операции с указанным классом, необходимо либо использовать этот класс в декларации ее параметров, либо сделать ее компонентом этого класса.
Форматы прототипа и определения функции-операции для перегрузки операции @ в общем виде выглядят следующим образом:
Value_type operator @ (parameter_list);
Value_type operator @ (parameter_list) { statements }
где operator – ключевое слово, предназначенное для обозначения функций-операций; value_type, parameter_list, statements – соответственно тип значения, список параметров и операторы тела функции-операции. В списке параметров функции-операции, в зависимости от характера, «арности» (местности) операции и способа определения функции, может быть от нуля до произвольного конечного числа параметров26 (в большинстве случаев, однако, число параметров не превышает двух).
Функцию-операцию всегда можно интерпретировать и вызвать как обычную функцию (в «функциональной» форме). Форматы «функционального» вызова функции-операции таковы:
// operator @ – компонентная функция
object_id.operator @(argument_list);
pointer_to_object_id –> operator @(argument_list)
(*pointer_to_object_id).operator @(argument_list)
// operator @ – глобальная функция
operator @(argument_list);
где object_id, pointer_to_object_id – имена соответственно объекта и указателя на объект, для которого вызывается функция-операция. Однако гораздо более удобна неявная, «операционная» форма вызова, при которой применяется привычный синтаксис работы с операциями:
@ argument // для унарной префиксной операции
argument @ // для унарной постфиксной операции
argument1 @ argument2 // для бинарной операции
Приведенный ниже пример показывает прототипы нескольких компонентных функций-операций, введенных для класса целочисленных прямоугольных матриц27. Далее дается иллюстрация к использованию этих функций.
Пример
class CIntMatrix { // класс целочисленных матриц
public:
...
// сумма матриц
const CIntMatrix operator + (const CIntMatrix & m2) const;
// произведение матриц
const CIntMatrix operator * (const CIntMatrix & m2) const;
// присваивание
CIntMatrix & operator = (const CIntMatrix & m2);
// умножение элементов на -1
const CIntMatrix & operator - ();
...
};
...
// использование функций-операций в операционной форме
{
CIntMatrix m1(/*...*/),m2(/*...*/),m3(/*...*/);
...
m3 = m1 + (-m2) * m3;
...
}
// использование функций-операций в функциональной форме
{
CIntMatrix m1(/*...*/),m2(/*...*/),m3(/*...*/);
...
// эквивалентная функциональная форма оператора m3 = m1 + (-m2) * m3;
m3.operator=(m1.operator+((m2.operator-()).operator*(m3)));
...
}
...
Прежде чем более детально обсуждать способы перегрузки операций, остановимся на ряде наиболее важных ограничений механизма перегрузки операций С++.
Во-первых, перегружать можно только стандартные операции; вводить новые символы операций С++ не позволяет. Во-вторых, перегрузке в С++ подлежат не все стандартные операции. К числу операций, не подлежащих перегрузке, относятся следующие:
. (уточнение имени);
.* (прямой доступ через указатель на компонент);
:: (уточнение контекста);
?: (условная операция);
# (запрет обработки аргументов макросов);
## (конкатенация лексем);
sizeof (вычисление размера операнда);
typeid (идентификация типа);
new, new[], delete, delete[] (операции управления памятью);
static_cast, dynamic_cast, const_cast, reinterpret_cast (операции преобразования типов). Остальные операции могут быть перегружены, причем такие операции, как &, *, +, –, допускают перегрузку как унарной, так и бинарной форм. В-третьих, то, что операция подлежит перегрузке, не означает, что она может быть всегда перегружена грамотно и безопасно; имеется ряд операций, безопасная перегрузка которых труднореализуема (их перечень будет рассмотрен далее). В-четвертых, при перегрузке нельзя изменить местность операции; нельзя также сменить уровень ее приоритета, ассоциативность и смысл содержащих ее выражений.
Теперь перейдем к детальному изучению способов перегрузки операций и затронем также некоторые «подводные камни», появляющиеся при перегрузке.
Практически любую из функций-операций можно ввести тремя разными способами: 1) как нестатическую компонентную функцию класса, на который выполняется перегрузка; 2) как глобальную (свободную) функцию, хотя бы один параметр которой имеет тип, связанный с требуемым классом; 3) как глобальную дружественную функцию к указанному классу. Поскольку 2-ой способ в большинстве случаев сводится к 3-му способу, фактически имеется два способа перегрузки: либо глобальной, либо нестатической компонентной функцией-операцией. Ниже отдельно рассматриваются варианты и правила перегрузки унарных, а затем бинарных операций (те операции, при перегрузке которых есть специальные ограничения, выделяются отдельно).
6.2. Перегрузка унарных операций
Любую префиксную унарную операцию @ можно перегрузить, определив либо нестатическую компонентную функцию-операцию без параметров, либо глобальную (возможно, дружественную) функцию с одним параметром. Форматы заголовков функций-операций в названных случаях будут следующими:
value_type operator @ (); // компонентная функция
value_type operator @ (parameter); // глобальная функция
friend value_type operator @ (parameter); // дружественная функция
где parameter – декларация параметра функции-операции (параметр должен быть связан с классом C, на который выполняется перегрузка операции, например, const C &); value_type – в общем случае произвольный тип возвращаемого значения (часто он также связан с классом C).
Если функция-операция определена как компонентная функция, то запись @x (x – объект класса C) будет интерпретирована как x.operator@() (в роли операнда операции выступает *this). Если же используется глобальная функция-операция, то имеет место интерпретация operator@(x) (операнд поступает в качестве единственного аргумента).
Ниже, для примера, дано определение функции-операции, перегружающей унарную операцию ! для стандартного шаблонного класса basic_ios (эта операция используется для проверки корректности работы с потоком ввода-вывода):
template<class charT, class traits>
inline bool basic_ios<charT, traits>::operator!() const
{