Выражения — различия между версиями
Korvin (обсуждение | вклад) м (→Операторы) |
Raw mat (обсуждение | вклад) |
||
(не показаны 12 промежуточных версий 8 участников) | |||
Строка 64: | Строка 64: | ||
function caller() { | function caller() { | ||
var object = new MyClass; | var object = new MyClass; | ||
− | + | puts(object.field); | |
object.field = "hello everyone!"; | object.field = "hello everyone!"; | ||
object.SetField("universe"); | object.SetField("universe"); | ||
Строка 74: | Строка 74: | ||
;1-7: Мы объявляем некоторый класс <tt>MyClass</tt>, который имеет поле ''m_Field'' и свойство ''field'', связанное с полем. Так же имеется метод <tt>SetField()</tt>, который устанавливает новое значение свойства, которое определяется выражением в строке 5. Значение свойства складывается из строковой константы <tt>"hello "</tt>, к которой добавляется значение параметра ''what''. | ;1-7: Мы объявляем некоторый класс <tt>MyClass</tt>, который имеет поле ''m_Field'' и свойство ''field'', связанное с полем. Так же имеется метод <tt>SetField()</tt>, который устанавливает новое значение свойства, которое определяется выражением в строке 5. Значение свойства складывается из строковой константы <tt>"hello "</tt>, к которой добавляется значение параметра ''what''. | ||
− | ;9-15: В этой функции приводится пример применения вышеописанного класса. Сначала мы создаем инстанцию класса <tt>MyClass</tt> с помощью оператора <tt>'''new'''</tt>. Затем вызывается функция <tt> | + | ;9-15: В этой функции приводится пример применения вышеописанного класса. Сначала мы создаем инстанцию класса <tt>MyClass</tt> с помощью оператора <tt>'''new'''</tt>. Затем вызывается функция <tt>puts()</tt>, которая печатает значение свойства объекта. В третьей строке мы присваиваем свойству <tt>field</tt> (а значит и полю ''m_Field'') новое значение. Последние две строки показывают пример вызова метода с явным аргументом и с аргументом по умолчанию. |
== Операторы == | == Операторы == | ||
− | С точки зрения языка К++, ''оператор'' — это специальная функция, имеющая строго определенное название, принимающая определенный набор параметров и возвращающая определенное значение. Фактически, операторы являются частью самого языка и обрабатываются отдельно от обычных функций еще на этапе синтаксического разбора программы, а следовательно, могут иметь любое имя (то есть, на них не распространяются правила именования [[Идентификатор|идентификаторов]]). Так и происходит. К примеру, все арифметические выражения что мы писали ранее, содержат наборы числовых констант, переменных и операторов, реализующих арифметические операции. То есть, <tt>+</tt>, <tt>-</tt>, <tt>*</tt>, <tt>/</tt>, <tt>++</tt> и многие другие — это операторы. Например, оператор <tt>+</tt>, объявленный в реализации [[Стандартная библиотека Gide|стандартной библиотеки]] для класса <tt>[[Стандартные типы данных#Целые числа|int]]</tt>, принимает в качестве параметра объект того же типа и выполняет операцию сложения. | + | С точки зрения языка К++, ''оператор'' — это специальная функция, имеющая строго определенное название, принимающая определенный набор параметров и возвращающая определенное значение. Фактически, операторы являются частью самого языка и обрабатываются отдельно от обычных функций еще на этапе синтаксического разбора программы, а следовательно, могут иметь любое имя (то есть, на них не распространяются правила именования [[Идентификатор|идентификаторов]]). Так и происходит. К примеру, все арифметические выражения что мы писали ранее, содержат наборы числовых констант, переменных и операторов, реализующих арифметические операции. То есть, <tt>+</tt>, <tt>-</tt>, <tt>*</tt>, <tt>/</tt>, <tt>++</tt> и многие другие — это операторы. Например, оператор <tt>+</tt>, объявленный в реализации [[Стандартная библиотека Gide|стандартной библиотеки]] для класса <tt>[[Стандартные типы данных#Целые числа|int]]</tt>, принимает в качестве параметра объект того же типа (<tt>int</tt>) и выполняет операцию сложения. |
− | Существует большое количество разнообразных операторов, которые разделяются на два больших класса: ''унарные'' и ''бинарные'' операторы. Первые работают с единственным объектом (то есть применяются для него же самого), вторые — оперируют двумя объектами. Таким образом, оператор <tt>+</tt> является бинарным оператором, в то время как <tt>++</tt> | + | Существует большое количество разнообразных операторов, которые разделяются на два больших класса: ''унарные'' и ''бинарные'' операторы. Первые работают с единственным объектом (то есть, применяются для него же самого), вторые — оперируют двумя объектами. Таким образом, оператор <tt>+</tt> является бинарным оператором, в то время как <tt>++</tt> является унарным. В языках программирования, операторы применяются для самых различных задач, а не только для арифметики. Например, в книге уже был рассмотрен оператор <tt>'''as'''</tt>, служащий для явного [[Приведение типов|приведения типов]]. |
− | Однако, то что операторы являются частью языка программирования, не означает что программист не может их использовать для собственных нужд в своих классах. Операторы, подобно обычным функциям и методам, позволяют | + | Однако, то что операторы являются частью языка программирования, не означает что программист не может их использовать для собственных нужд в своих классах. Операторы, подобно обычным функциям и методам, позволяют производить ''перегрузку'' и переобъявление. Приведем простой пример, иллюстрирующий этот механизм: |
<source lang="kpp" line="1"> | <source lang="kpp" line="1"> | ||
Строка 97: | Строка 97: | ||
x.field = "X"; | x.field = "X"; | ||
y.field = "Y"; | y.field = "Y"; | ||
− | + | puts(x += y); | |
− | + | puts(x += "Z"); | |
} | } | ||
</source> | </source> | ||
− | ;1-6: Используя [[Классы и объекты#Расширения|механизм расширений]], мы дополнили функциональность ранее описанного класса <tt>MyClass</tt>, добавив в него три оператора: две реализации бинарного оператора <tt>+=</tt> и оператор приведения типа. В блоке описания параметров для бинарных операторов мы указываем, с объектами какого типа должна работать данная конкретная реализация оператора. В первом случае, принимается объект нашего же класса <tt>MyClass</tt>, во втором случае — объект стандартного класса <tt>[[Стандартные типы данных#Строки|string]]</tt>. Оба оператора делают одно и то же: добавляют к текущему значению поля m_Field, значение содержащееся в аргументе, то есть, в другом объекте, расположенном справа от оператора. Обратите внимание на тела операторов: для ''конкатенации'' строк применяется тот же самый оператор +=, однако следует понимать, что это уже применяется реализация оператора, объявленная в классе строки. | + | ;1-6: Используя [[Классы и объекты#Расширения|механизм расширений]], мы дополнили функциональность ранее описанного класса <tt>MyClass</tt>, добавив в него три оператора: две реализации бинарного оператора <tt>+=</tt> и оператор приведения типа. В блоке описания параметров для бинарных операторов мы указываем, с объектами какого типа должна работать данная конкретная реализация оператора. В первом случае, принимается объект нашего же класса <tt>MyClass</tt>, во втором случае — объект стандартного класса <tt>[[Стандартные типы данных#Строки|string]]</tt>. Оба оператора делают одно и то же: добавляют к текущему значению поля ''m_Field'', значение содержащееся в аргументе, то есть, в другом объекте, расположенном справа от оператора. Обратите внимание на тела операторов: для ''конкатенации'' строк применяется тот же самый оператор +=, однако следует понимать, что это уже применяется реализация оператора, объявленная в классе строки. |
: Оператор приведения типов служит для преобразования объекта нашего класса <tt>MyClass</tt> к классу строки. Это может быть необходимо, например, для удобного вывода информации об обекте. При этом, инстанция класса указывается в выражении так, как будто она является строкой. Соответственно, ее можно будет передавать в качестве параметра функциям, принимающим строки — сперва будет выполнена операция приведения типа (с помощью нашего оператора), а затем результат будет передан как параметр. Стоит отметить, что эта операция выполняется компилятором автоматически. От нас требуется только реализовать сам оператор приведения типа. | : Оператор приведения типов служит для преобразования объекта нашего класса <tt>MyClass</tt> к классу строки. Это может быть необходимо, например, для удобного вывода информации об обекте. При этом, инстанция класса указывается в выражении так, как будто она является строкой. Соответственно, ее можно будет передавать в качестве параметра функциям, принимающим строки — сперва будет выполнена операция приведения типа (с помощью нашего оператора), а затем результат будет передан как параметр. Стоит отметить, что эта операция выполняется компилятором автоматически. От нас требуется только реализовать сам оператор приведения типа. | ||
Строка 109: | Строка 109: | ||
;8-12: Для иллюстрирования вышесказанного, мы создаем два экземпляра нашего класса и присваиваем свойству <tt>field</tt> новые значения: "X" и "Y" соответственно. | ;8-12: Для иллюстрирования вышесказанного, мы создаем два экземпляра нашего класса и присваиваем свойству <tt>field</tt> новые значения: "X" и "Y" соответственно. | ||
− | ;13: Эта строка показывает наш оператор в действии. Как видите, мы выполняем операцию += для двух инстанций нашего класса. При этом, компилятор языка проверит, поддерживает ли наш класс такой оператор для аргумента типа <tt>MyClass</tt>. В нашем случае это так. Следовательно, при вычислении выражения он будет задействован. Результат выполнения операции <tt>x += y</tt> так же является объектом класса <tt>MyClass</tt> (на самом деле, это будет все та же инстанция ''x'', поскольку в реализации оператора применена конструкция "<tt>'''return this''';</tt>"). Но функция <tt> | + | ;13: Эта строка показывает наш оператор в действии. Как видите, мы выполняем операцию += для двух инстанций нашего класса. При этом, компилятор языка проверит, поддерживает ли наш класс такой оператор для аргумента типа <tt>MyClass</tt>. В нашем случае это так. Следовательно, при вычислении выражения он будет задействован. Результат выполнения операции <tt>x += y</tt> так же является объектом класса <tt>MyClass</tt> (на самом деле, это будет все та же инстанция ''x'', поскольку в реализации оператора применена конструкция "<tt>'''return this''';</tt>"). Но функция <tt>puts()</tt> принимает в качестве параметра строку, значит необходимо выполнить операцию приведения типов. Компилятор языка К++ вторично обратится за помощью к самому классу и проверит наличие оператора приведения к типу <tt>string</tt>. К счастью, такой оператор тоже имеется, поэтому компилятор заключает что типы ''приводимы'', и генерирует вызов оператора приведения. В заключение, результат выполнения оператора приведения (в нашем случае это будет строка "XY") будет передан функции <tt>puts()</tt>, которая уже распечатает его. |
;14: Смысл этой конструкции мало чем отличается от предыдущей, за исключением того, что вторым ''операндом'' указан не объект класса <tt>MyClass</tt>, а строка. Соответственно, при компиляции будет задействована вторая реализация оператора <tt>+=</tt>; в остальном все просиходит точно так же. | ;14: Смысл этой конструкции мало чем отличается от предыдущей, за исключением того, что вторым ''операндом'' указан не объект класса <tt>MyClass</tt>, а строка. Соответственно, при компиляции будет задействована вторая реализация оператора <tt>+=</tt>; в остальном все просиходит точно так же. | ||
− | '''Примечание:''' Здесь мы затронули лишь самые общие сведения об операторах и их применении. На самом деле, это очень обширная тема, требующая отдельного рассмотрения. Более подробно, про операторы можно почитать в [[ | + | '''Примечание:''' Здесь мы затронули лишь самые общие сведения об операторах и их применении. На самом деле, это очень обширная тема, требующая отдельного рассмотрения. Более подробно, про операторы можно почитать в [[K++#Операторы|специальном разделе]], целиком посвященном вопросам операторов. |
Текущая версия на 13:45, 13 июля 2013
Как уже было упомянуто в предыдущих главах (в истории ООП), одним из наиболее значимых нововведений языков программрования высокого уровня, стала возможность записи арифметических выражений в естественной для человека форме, с использованием операторов и функций. Для того чтобы посчитать значение выражения, теперь программисту требуется лишь правильно записать его, не заботясь о том, как оно будет вычисляться. Всю работу по разбору такого выражения, определению очередности выполнения операций берет на себя компилятор языка.
В этой главе мы узнаем, что такое выражения с точки зрения языка К++, как они формируются и на что нужно обращать внимание при их записи. Кратко будет затронута тема операторов, будут даны основные сведения о них. Более подробно, операторы как элемент языка К++, будут рассмотрены в конце книги, в отдельном разделе.
Содержание |
[править] Арифметические операции
Наиболее простой и понятный тип выражений — это самые обычные арифметические выражения:
<source lang="kpp"> var x = 2 + 3 * 4; var y = (2 + 3) * 4; </source>
Для простоты мы используем хорошо нам известную конструкцию объявления переменной, но на этот раз в инициализаторе мы указываем не одну константу, вроде строки "hello world", а целое выражение. Выражением считается все, что записано между знаком "=" и точкой с запятой ";", которая завершает конструкцию.
При вычислении значения выражения применяются те же самые правила что и в обычной математике, то есть операции имеют приоритет и выполняются строго в порядке убывания приоритета. В первом случае, сначала будет вычислено подвыражение 3 * 4, к значению которого будет добавлено число 2; сумма будет установлена как значение переменной. Во втором случае, приоритет операций был изменен введением круглых скобок, которые имеют тот же смысл, что и в математике: сначала вычисляется значение в скобках, а затем остальные операции, опять же в порядке их приоритета. Скобок в выражение может быть сколько угодно, допускается вложенность.
...Таким образом, переменной x будет присвоено значение 14, а переменная y будет равна 20.
В выражениях могут указываться не только числовые константы. Так же как и в математике, в выражениях можно использовать переменные.
При вычислении выражения вмсето имени переменной подставляется ее значение. Таким образом, если в ходе рассчетов выражения, переменная изменит свое значение, то вместе следующего вхождения переменной может быть подставлено уже новое значение. А чтобы с уверенностью сказать "может быть подставлено" или "будет подставлено" нужно смотреть, какой именно код записан в выражении. С этим моментом связана известная проблема неоднозначности выражений. Забежав вперед, можно рассмотреть связанный с данным вопросом пример применения операторов в выражении:
<source lang="kpp"> var i = 5; var x = ++i + ++i; </source>
Как вы думаете, какое значение будет иметь переменная x после выполнения вышеприведенного кода? Программист, знающий только Паскаль при виде этого примера скорее всего впадет в ступор, поскольку этот язык не имеет оператора ++, либо он сочтет что выражение записано ошибочно.
Новичок, изучающий язык C++, но уже кое-что знающий о нем, может рассуждать так: «Оператор ++ — это оператор инкремента. В зависимости от того где он расположен в выражении, будет зависеть то как он изменяет переменную; если оператор находится слева он переменной, то сначала будет произведена операция инкремента, а затем новое значение будет использоваться при расчете выражения. В нашем случае это именно так. Стало быть, при рассчете выражения, в обоих случаях значение переменной i будет увеличено на единицу и будет использовано в рассчете. В результате мы получим выражение x = 6 + 7 = 13»
Опытный программист на C++, первым делом спросит как реализованы операторы ++ и +, и происходит ли копирование аргументов. Если копирование происходит, то значение будет таким же как у новичка, то есть 13. Если же оператор работает с самой переменной, то произойдет следующее:
Первый оператор ++ увеличит значение переменной i на единицу, которое теперь будет равно 6. Поскольку мы имеем дело со ссылкой на переменную то это значение не будет нигде сохраняться. Далее, при вычислении значения второго слагаемого сработает уже второй оператор ++, который так же увеличит (уже увеличенное значение!) переменную на 1, и вернет результат 6 + 1, то есть 7. При вычислении итогового значения мы получаем выражение 7 + 7, то есть 14.
Получается что сколько человек, столько и мнений. А самое интересное, что каждый из них прав. Их рассуждения логичны с учетом того, с какой позиции смотреть на проблему. В такой неразберихе не мудрено запутаться. Если читатель еще не сбит с толку окончательно, то можно продолжить и рассмотреть эту задачу с точки зрения языка К++.
В случае с языком К++ вычисление значения будет происходить по второму сценарию. То есть, результат такого выражения будет равен 14.
[править] Вызов функций
Вызов функций в выражениях осуществляется путем записи имени функции, следом за которым в круглых скобках перечисляются ее аргументы. При этом, при вычислении выражения, вместо самой функции будет подставлено ее значение:
<source lang="kpp"> const pi = 3.1415926; var x = 10; var y = x * sin(pi); </source>
[править] Доступ к полям
Доступ к полям и свойствам классов осуществляется путем записи имени объекта, следом закоторым ставится оператор точка ".", а затем идентификатор поля либо метода. Если ссылаемое поле само имеет свойства или поля, то к ним так же можно ссылаться, поставив вторую точку и указав идентификатор и т. д.
<source lang="kpp" line="1"> class MyClass {
var m_Field = "hello world"; property field read m_Field write m_Field; public function SetField(const string what = "world") { m_Field = "hello " + what; }
}
function caller() {
var object = new MyClass; puts(object.field); object.field = "hello everyone!"; object.SetField("universe"); object.SetField();
} </source>
- 1-7
- Мы объявляем некоторый класс MyClass, который имеет поле m_Field и свойство field, связанное с полем. Так же имеется метод SetField(), который устанавливает новое значение свойства, которое определяется выражением в строке 5. Значение свойства складывается из строковой константы "hello ", к которой добавляется значение параметра what.
- 9-15
- В этой функции приводится пример применения вышеописанного класса. Сначала мы создаем инстанцию класса MyClass с помощью оператора new. Затем вызывается функция puts(), которая печатает значение свойства объекта. В третьей строке мы присваиваем свойству field (а значит и полю m_Field) новое значение. Последние две строки показывают пример вызова метода с явным аргументом и с аргументом по умолчанию.
[править] Операторы
С точки зрения языка К++, оператор — это специальная функция, имеющая строго определенное название, принимающая определенный набор параметров и возвращающая определенное значение. Фактически, операторы являются частью самого языка и обрабатываются отдельно от обычных функций еще на этапе синтаксического разбора программы, а следовательно, могут иметь любое имя (то есть, на них не распространяются правила именования идентификаторов). Так и происходит. К примеру, все арифметические выражения что мы писали ранее, содержат наборы числовых констант, переменных и операторов, реализующих арифметические операции. То есть, +, -, *, /, ++ и многие другие — это операторы. Например, оператор +, объявленный в реализации стандартной библиотеки для класса int, принимает в качестве параметра объект того же типа (int) и выполняет операцию сложения.
Существует большое количество разнообразных операторов, которые разделяются на два больших класса: унарные и бинарные операторы. Первые работают с единственным объектом (то есть, применяются для него же самого), вторые — оперируют двумя объектами. Таким образом, оператор + является бинарным оператором, в то время как ++ является унарным. В языках программирования, операторы применяются для самых различных задач, а не только для арифметики. Например, в книге уже был рассмотрен оператор as, служащий для явного приведения типов.
Однако, то что операторы являются частью языка программирования, не означает что программист не может их использовать для собственных нужд в своих классах. Операторы, подобно обычным функциям и методам, позволяют производить перегрузку и переобъявление. Приведем простой пример, иллюстрирующий этот механизм:
<source lang="kpp" line="1"> extend MyClass { public:
operator MyClass += (const MyClass mc) { m_Field += mc.field; return this; } operator MyClass += (const string s) { m_Field += s; return this; } const operator string () { return m_Field; }
}
function f() {
var x = new MyClass; var y = new MyClass; x.field = "X"; y.field = "Y"; puts(x += y); puts(x += "Z");
} </source>
- 1-6
- Используя механизм расширений, мы дополнили функциональность ранее описанного класса MyClass, добавив в него три оператора: две реализации бинарного оператора += и оператор приведения типа. В блоке описания параметров для бинарных операторов мы указываем, с объектами какого типа должна работать данная конкретная реализация оператора. В первом случае, принимается объект нашего же класса MyClass, во втором случае — объект стандартного класса string. Оба оператора делают одно и то же: добавляют к текущему значению поля m_Field, значение содержащееся в аргументе, то есть, в другом объекте, расположенном справа от оператора. Обратите внимание на тела операторов: для конкатенации строк применяется тот же самый оператор +=, однако следует понимать, что это уже применяется реализация оператора, объявленная в классе строки.
- Оператор приведения типов служит для преобразования объекта нашего класса MyClass к классу строки. Это может быть необходимо, например, для удобного вывода информации об обекте. При этом, инстанция класса указывается в выражении так, как будто она является строкой. Соответственно, ее можно будет передавать в качестве параметра функциям, принимающим строки — сперва будет выполнена операция приведения типа (с помощью нашего оператора), а затем результат будет передан как параметр. Стоит отметить, что эта операция выполняется компилятором автоматически. От нас требуется только реализовать сам оператор приведения типа.
- 8-12
- Для иллюстрирования вышесказанного, мы создаем два экземпляра нашего класса и присваиваем свойству field новые значения: "X" и "Y" соответственно.
- 13
- Эта строка показывает наш оператор в действии. Как видите, мы выполняем операцию += для двух инстанций нашего класса. При этом, компилятор языка проверит, поддерживает ли наш класс такой оператор для аргумента типа MyClass. В нашем случае это так. Следовательно, при вычислении выражения он будет задействован. Результат выполнения операции x += y так же является объектом класса MyClass (на самом деле, это будет все та же инстанция x, поскольку в реализации оператора применена конструкция "return this;"). Но функция puts() принимает в качестве параметра строку, значит необходимо выполнить операцию приведения типов. Компилятор языка К++ вторично обратится за помощью к самому классу и проверит наличие оператора приведения к типу string. К счастью, такой оператор тоже имеется, поэтому компилятор заключает что типы приводимы, и генерирует вызов оператора приведения. В заключение, результат выполнения оператора приведения (в нашем случае это будет строка "XY") будет передан функции puts(), которая уже распечатает его.
- 14
- Смысл этой конструкции мало чем отличается от предыдущей, за исключением того, что вторым операндом указан не объект класса MyClass, а строка. Соответственно, при компиляции будет задействована вторая реализация оператора +=; в остальном все просиходит точно так же.
Примечание: Здесь мы затронули лишь самые общие сведения об операторах и их применении. На самом деле, это очень обширная тема, требующая отдельного рассмотрения. Более подробно, про операторы можно почитать в специальном разделе, целиком посвященном вопросам операторов.