Полезное:
Как сделать разговор полезным и приятным
Как сделать объемную звезду своими руками
Как сделать то, что делать не хочется?
Как сделать погремушку
Как сделать так чтобы женщины сами знакомились с вами
Как сделать идею коммерческой
Как сделать хорошую растяжку ног?
Как сделать наш разум здоровым?
Как сделать, чтобы люди обманывали меньше
Вопрос 4. Как сделать так, чтобы вас уважали и ценили?
Как сделать лучше себе и другим людям
Как сделать свидание интересным?
Категории:
АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника
|
Расширение действия (перегрузка) стандартных операций
Одной из привлекательных особенностей языка Си++ является возможность распространения действия стандартных операций на операнды, для которых эти операции первоначально в языке не предполагались. Например, определив новый тип комплексное число нам наверняка захочется иметь возможность записать сумму двух объектов класса комплексное число (comp) в виде s+g. Для того чтобы это стало возможным программист должен определить специальную функцию, называемую "операция-функция" (operator function). Формат определения операции-функции: тип_функции operator# (список формальных параметров) {операторы_тела_операции-функции} При необходимости может добавляться и прототип операции-функции с таким форматом: тип_функции operator#(список формальных параметров); где # - знак операции, например, =, +, -, и т.д.; operator# - имя функции. Как мы видим определение и описание операции-функции практически ни чем не отличается от обычной функции, но отличия все-таки имеются, и состоят они в наличии двух абсолютно эквивалентных способах вызова оператора-функции. Так для операции сложения двух комплексных чисел после определения операции-функции comp operator+(comp s, comp g){ comp d; d.Re = s.Re+g.Re; d.Im= s.Im+g.Im; return d; } два способа её вызова выглядя следующим образом: comp k(7,2), e(2,5); operator+(k, e); //первый способ вызова k+e; //второй способ вызова как видно из примера первый способ очень похож на обычный вызов обычной глобальной функции имя_функции(список фактических параметров), а второй способ более краткий позволяет добиться от типов данных, определённых программистом, такой же функциональности как и у фундаментальных типов данных, т.е. перегрузить для новых типов данных необходимый им набор, определённых в языке Си++ операций. В отличии от обычных функций при реализации операция-функция надо учитывать несколько ограничений, так количество параметров у операции-функции зависит от арности операции и от способа определения функции. Операция-функция определяет алгоритм выполнения перегруженной операции, когда эта операция применяется к объектам класса, для которого операция-функция введена. Чтобы явная связь с классом была обеспечена, операция функция должна быть либо компонентом класса, либо она должна быть определена в классе как дружественная, либо у нее должен быть хотя бы один параметр типа класс (или ссылка на класс). Начнем с последнего варианта. Если для класса Т введена операция-функция с заголовком Т operator *(T х, T у) и определены два объекта А, В класса Т, то выражение А*В интерпретируется как вызов функции operator * (А,B). Вторую возможность перегрузки бинарной операции представляют компонентные функции классов. Любая стандартная бинарная операция @ может быть перегружена с помощью нестатической операции-функции, входящей в число компонентов класса. В этом случае у нее должен быть только один параметр и заголовок может выглядеть так: Т operator @ (Т х) (здесь Т - определенный пользователем тип, т.е. класс). В этом случае выражение A@B c объектами А, В класса T в качестве операндов интерпретируется как вызов функции А.operator@(В), причем в теле операции-функции выполняется обработка компонентов объекта-параметра В и того объекта А, для которого осуществлен вызов. При необходимости принадлежность компонентов объекту А в теле операции-функции можно сделать явным с помощью указателя this. Итак, механизм классов дает возможность программисту определять новые типы данных, отображающие понятия решаемой задачи. Перегрузка стандартных операций языка Си++ позволяет сделать операции над объектами новых классов удобными и общепонятными. Но возникают два вопроса. Можно ли вводить собственные обозначения для операций, не совпадающие со стандартными операциями языка Си++? И все ли операции языка Си++ могут быть перегружены? К сожалению (или как констатация факта), вводить операции с совершенно новыми обозначениями язык Си++ не позволяет. Ответ на второй вопрос также отрицателен - существует несколько операций, не допускающих перегрузки. Вот их список: прямой выбор компонента структурированного объекта; . * обращение к компоненту через указатель на него; ?: условная операция; :: операция указания области видимости; sizeof операция вычисления размера в байтах; # препроцессорная операция; ## препроцессорная операция. Рассмотрим еще несколько важных особенностей механизма перегрузки (расширения действия) стандартных операций языка Си++. При расширении действия (при перегрузке) стандартных операций нельзя и нет возможности изменять их приоритеты. Нельзя изменить для перегруженных операций синтаксис выражений, т.е. невозможно ввести унарную операцию = или бинарную операцию ++. Нельзя вводить новые лексические обозначения операций, даже формируя их из допустимых символов. Например, возведение в степень ** из языка Фортран нельзя ввести в языке Си++. Любая бинарная операция @ определяется для объектов некоторого класса двумя существенно разными способами: либо как компонентная функция с одним параметром, либо как глобальная (возможно дружественная) функция с двумя параметрами. В первом случае х@у означает вызов х.operator@(у), во втором случае х@у означает вызов operator@(x,y). В соответствии с семантикой бинарных операций ' = ', ' [ ] ', ' ->' операции-функции с названиями operator =, operator [ ], operator -> не могут быть глобальными функциями, а должны быть нестатическими компонентными функциями. "Это гарантирует, что первыми операндами будут lvalue". Любая унарная операция '$' определяется для объектов некоторого класса также двумя способами: либо как компонентная функция без параметров, либо как глобальная (возможно дружественная) функция с одним параметром. Для префиксной операции ' $' выражение $z означает вызов компонентной функции z..operator $ () или вызов глобальной функции operator $(z). Для постфиксной операции выражение z$ означает либо вызов компонентной функции z.operator$(), либо вызов глобальной функции operator$(z). Синтаксис языка Си++ определяет некоторые встроенные операции над стандартными типами как комбинации других встроенных операций над теми же операндами. Например, для переменной long m = 0; выражение ++m означает m += 1, что в свою очередь означает выполнение выражения m = m + 1. Такие автоматические замены выражений не реализуются и не справедливы для перегруженных операций. Например, в общем случае определение operator *=() нельзя вывести из определений operator * () и operator = (). Нельзя изменить смысл выражения, если в него не входит объект класса, введенного пользователем. В частности, нельзя определить операцию-функцию, действующую только на указатели. Невозможно для операнда m типа int изменить смысл выражения 2 + m и т.п. "Операция-функция, первым параметром которой предполагается основной (стандартный) тип, не может быть компонентной функцией". Для объяснения этого ограничения предположим, что аа - объект некоторого класса и для него расширено действие операции ' + '. При разборе выражения аа + 2 компилятором выполняется вызов операции-функции аа. operator + (2) или operator +(aa,2). При разборе 2 + аа допустим вызов operator + (2,аа), но ошибочен 2.operator + (аа). Таким образом, расширение действия операции + на выражение стандартный _тип + объект_класса допустимо только с помощью глобальных операций-функций. При расширении действия операций приходится предусматривать всевозможные сочетания типов операндов. Например, определяя операцию сложения ' + ' для комплексных чисел, приходится учитывать сложение комплексного числа с вещественным и вещественного с комплексным, комплексного с целым и целого с комплексным и т.д. Если учесть, что вещественные числа представлены несколькими ти-пами (float, double, long double) и целые числа имеют разные типы (int, long, unsigned, char), то оказывается необходимым ввести большое количество операций-функций. К счастью, при вызове операций-функций действуют все соглашения о преобразованиях стандартных типов параметров, и нет необходимости учитывать сочетания всех типов. В ряде случаев для бинарной операции достаточно определить только три варианта: • стандартный_тип, класс • класс, стандартный_тип • класс, класс. Например, для рассмотренного класса comp можно ввести как дружественные такие операции-функции: comp operator +(comp x, comp у) {return(comp(x.real + y.real, x.imag + y.imag)); } comp operator + (double x, complex y) {return(comp(x + y.real, y.imag));) comp operator + (complex x, double y) { return(comp(x.real + y, x.imag));} После этого станут допустимыми выражения в следующих операторах: comp СС(1.О,2.0); comp ЕЕ; ЕЕ. = 4.0 + СС; ЕЕ = ЕЕ + 2.0; ЕЕ = СС + ЕЕ; ЕЕ = СС + 20; // По умолчанию приведение int к double СС = ЕЕ + 'е'; //По умолчанию приведение char к double Вместо использования нескольких (в нашем примере вместо трех) очень схожих операций-функций можно задачу преобразования стандартного типа в объект класса поручить конструктору. Для этого требуется только одно - необходим конструктор, формирующий объект класса по значению стандартного типа. Например, добавление в класс complex такого конструктора comp (double x) { real = x; imag =0.0; } позволяет удалить все дополнительные операции-функции, оставив только одну с прототипом: friend comp operator +(comp, comp); В этом случае целый операнд выражения 6+ЕЕ автоматически преобразуется к типу double, а затем конструктор формирует комплексное число с нулевой мнимой частью. Далее выполняется операция-функция operator +(comp(double(6),double(0)), ЕЕ) Вместо включения в класс дополнительного конструктора с одним аргументом можно в заголовке единственного конструктора ввести умалчиваемое значение второго параметра: compl(double r, double i = 0.0) { Re=r; Im = i; } Теперь каждое выражение с операцией ' + ', в которое входит, кроме объекта класса comp, операнд одного из стандартных типов, будет обрабатываться совершенно верно. Однако такое умалчивание является частным решением и не для всех классов пригодно. В отличие от всех других унарных операций операции ++ и --имеют, кроме префиксной формы еще и постфиксную. Это привело к особенностям при их перегрузке. В начальных версиях языка Си++ при перегрузках операций ++ и -- не делалось различия между постфиксной и префиксной формами. В современной версии языка Си++ принято соглашение, что перегрузка префиксных операций ++ и -- ничем не отличается от перегрузки других унарных операций, т.е. глобальные и, возможно, дружественные функции operator ++() и operator - -() с одним параметром некоторого класса определяют префиксные операции ++ и --. Компонентные операции-функции без параметров определяют те же префиксные операции. При расширении действия постфиксных операций ++ и -- операции-функции должны иметь еще один дополнительный параметр типа int. Если для перегрузки используется компонентная операция-функция, то она должна иметь один параметр типа int. Если операция-функция определена как глобальная (не компонентная), то ее первый параметр должен иметь тип класса, а второй - тип int. Когда в программе используется соответствующее постфиксное выражение, то операция-функция вызывается с нулевым целым параметром.
Date: 2015-05-22; view: 758; Нарушение авторских прав |