стандартного ввода:
int main(int argc, char* argv[])
{
switch(argc) {
case 1: // читать из стандартного ввода
break;
case 2: // читать параметр строку
cin = *new istream(strlen(argv[1]),argv[1]);
break;
default:
error("слишком много параметров");
return 1;
}
// как раньше
}
Программа осталась без изменений, за исключением добавления в
main() параметров и использования этих параметров в операторе
switch. Можно было бы легко модифицировать main() так, чтобы она
получала несколько параметров командной строки, но это оказывается
ненужным, особенно потому, что несколько выражений можно передавать
как один параметр:
dc "rate=1.1934;150/rate;19.75/rate;217/rate"
Здесь кавычки необходимы, поскольку ; является разделителем
команд в системе UNIX.
3.2 Краткая сводка операций
Операции C++ подробно и систематически описываются в #с.7;
прочитайте, пожалуйста, этот раздел. Здесь же приводится краткая
сводка и некоторые примеры. После каждой операции приведено одно
или более ее общеупотребительных названий и пример ее
использования. В этих примерах имя_класса - это имя класса, член -
имя члена, объект - выражение, дающее в результате объект класса,
указатель - выражение, дающее в результате указатель, выр -
выражение, а lvalue - выражение, денотирующее неконстантный объект.
Тип может быть совершенно произвольным именем типа (со *, () и
т.п.) только когда он стоит в скобках, во всех остальных случаях
существуют ограничения.
- стр 90 -
Унарные операции и операции присваивания правоассоциативны, все
остальные левоассоциативны. Это значит, что a=b=c означает a=(b=c),
a+b+c означает (a+b)+c, и *p++ означает *(p++), а не (*p)++.
Сводка Операций (часть 1)
---------------------------------------------------------------
-----
:: разрешение области видимости имя_класса :: член
:: глобальное :: имя
---------------------------------------------------------------
-----
-> выбор члена указатель->член
[] индексация указатель [ выр ]
() вызов функции выр (список_выр)
() построение значения тип (список_выр)
sizeof размер объекта sizeof выр
sizeof размер типа sizeof ( тип )
---------------------------------------------------------------
-----
++ приращение после lvalue++
++ приращение до ++lvalue
-- уменьшение после lvalue--
-- уменьшение до --lvalue
~ дополнение ~ выр
! не ! выр
- унарный минус - выр
+ унарный плюс + выр
& адрес объекта & lvalue
* разыменование * выр
new создание (размещение) new тип
delete уничтожение (освобождение) delete указатель
delete[] уничтожение вектора delete[ выр ]
указатель
() приведение (преобразование типа) ( тип ) выр
---------------------------------------------------------------
-----
* умножение выр * выр
/ деление выр / выр
% взятие по модулю (остаток) выр % выр
---------------------------------------------------------------
-----
+ сложение (плюс) выр + выр
- вычитание (минус) выр - выр
В каждой отчерченной части находятся операции с одинаковым
приоритетом. Операция имеет приоритет больше, чем операции из
частей, расположенных ниже. Например: a+b*c означает a+(b*c), так
как * имеет приоритет выше, чем +, а a+b-c означает (a+b)-c,
поскольку + и - имеют одинаковый приоритет (и поскольку +
левоассоциативен).
- стр 91 -
Сводка Операций (часть 2)
---------------------------------------------------------------
-----
<< сдвиг влево lvalue << выр
>> сдвиг вправо lvalue >> выр
---------------------------------------------------------------
-----
< меньше выр < выр
<= меньше или равно выр <= выр
> больше выр > выр
>= больше или равно выр >= выр
---------------------------------------------------------------
-----
== равно выр == выр
!= не равно выр != выр
---------------------------------------------------------------
-----
& побитовое И выр & выр
---------------------------------------------------------------
-----
^ побитовое исключающее ИЛИ выр ^ выр
---------------------------------------------------------------
-----
| побитовое включающее ИЛИ выр | выр
---------------------------------------------------------------
-----
&& логическое И выр && выр
---------------------------------------------------------------
-----
|| логическое включающее ИЛИ выр || выр
---------------------------------------------------------------
-----
? : арифметический if выр ? выр : выр
---------------------------------------------------------------
-----
= простое присваивание lvalue = выр
*= умножить и присвоить lvalue = выр
/= разделить и присвоить lvalue /= выр
%= взять по модулю и присвоить lvalue %= выр
+= сложить и присвоить lvalue += выр
-= вычесть и присвоить lvalue -= выр
<<= сдвинуть влево и присвоить lvalue <<= выр
>>= сдвинуть вправо и присвоить lvalue >>= выр
&= И и присвоить lvalue &= выр
|= включающее ИЛИ и присвоить lvalue |= выр
^= исключающее ИЛИ и присвоить lvalue ^= выр
---------------------------------------------------------------
-----
, запятая (последование) выр , выр
- стр 92 -
3.2.1 Круглые скобки
Скобками синтаксис C++ злоупотребляет; количество способов их
использования приводит в замешательство: они применяются для
заключения в них параметров в вызовах функций, в них заключается
тип в преобразовании типа (приведении к типу), в именах типов для
обозначения функций, а также для разрешения конфликтов приоритетов.
К счастью, последнее требуется не слишком часто, потому что уровни
приоритета и правила ассоциативности определены таким образом,
чтобы выражения "работали ожидаемым образом" (то есть, отражали
наиболее привычный способ употребления). Например, значение
if (i<=0 || max
3.2.2 Порядок вычисления
Порядок вычисления подвыражений в выражении неопределен. Например
int i = 1;
v[i] = i++;
- стр 93 -
может вычисляться или как v[1]=1, или как v[2]=1. При отсутствии
ограничений на порядок вычисления выражения может генерироваться
более хороший код. Было бы замечательно, если бы компилятор
предупреждал о подобных неоднозначностях, но большинство
компиляторов этого не делают.
Относительно операций
, && ||
гарантируется, что их левый операнд вычисляется раньше, чем правый.
Например, b=(a=2,a=1) присвоит b 3. В #3.3.1 приводятся примеры
использования && и ||. Заметьте, что операция последования ,
(запятая) логически отличается от запятой, которая используется для
разделения параметров в вызове функции. Рассмотрим
f1(v[i],i++); // два параметра
f2( (v[i],i++) ) // один параметр
В вызове f1 два параметра, v[i] и i++, и порядок вычисления
выражений-параметров неопределен. Зависимость выражения-параметра
от порядка вычисления - это очень плохой стиль, а также
непереносимо. В вызове f2 один параметр, выражение с запятой,
которое эквивалентно i++.
С помощью скобок нельзя задать порядок вычисления. Например,
a*(b/c) может вычисляться и как (a*b)/c, поскольку * и / имеют
одинаковый приоритет. В тех случаях, когда важен порядок
вычисления, можно вводить дополнительную переменную, например,
(t=b/c,a*t).
3.2.2 Увеличение и уменьшение*
Операция ++ используется для явного выражения приращения вместо
его неявного выражения с помощью комбинации сложения и
присваивания. По определению ++lvalue означает lvalue+=1, что в
свою очередь означает lvalue=lvalue+1 при условии, что lvalue не
вызывает никаких побочных эффектов. Выражение, обозначающее
(денотирующее) объект, который должен быть увеличен, вычисляется
один раз (только). Аналогично, уменьшение выражается операцией --.
Операции ++ и -- могут применяться и как префиксные, и как
постфиксные. Значением ++x является новое (то есть увеличенное)
значение x. Например, y=++x эквивалентно y=(x+=1). Значение x++,
напротив, есть старое значение x. Например, y=x++ эквивалентно
y=(t=x,x+=1,t), где t - переменная того же типа, что и x.
Операции приращения особенно полезны для увеличения и уменьшения
переменных в циклах. Например, оканчивающуюся нулем строку можно
копировать так:
inline void cpy(char* p, const char* q)
{
while (*p++ = *q++) ;
}
____________________
* Следовало бы переводить как "инкремент" и "декремент", однако
мы следовали терминологии, принятой в переводной литературе по C,
поскольку эти операции унаследованы от C. (прим. перев.)
- стр 94 -
Напомню, что увеличение и уменьшение указателей, так же как
сложение и вычитание указателей, осуществляется в терминах
элементов вектора, на которые указывает указатель; p++ приводит к
тому, что p указывает на следующий элемент. Для указателя p типа T*
по определению выполняется следующее:
long(p+1) == long(p)+sizeof(T);
3.2.4 Побитовые логические операции
Побитовые логические операции
& | ^ ~ >> <<
применяются к целым, то есть к объектам типа char, short, int, long
и их unsigned аналогам, результаты тоже целые.
Одно из стандартных применений побитовых логических операций -
реализация маленькиого множества (вектора битов). В этом случае
каждый бит беззнакового целого представляет один член множества, а
число членов ограничено числом битов. Бинарная операция &
интерпретируется как пересечение, | как объединение, а ^ как
разность. Для именования членов такого множества можно использовать
перечисление. Вот маленький пример, заимствованный из реализации
(не пользовательского интерфейса) :
enum state_value { _good=0, _eof=1, _fail=2, _bad=4};
// хорошо, конец файла, ошибка, плохо
Определение _good не является необходимым. Я просто хотел, чтобы
состояние, когда все в порядке, имело подходящее имя. Состояние
потока можно установить заново следующим образом:
cout.state = _good;
Например, так можно проверить, не был ли испорчен поток или
допущена операционная ошибка:
if (cout.state&(_bad|_fail)) // не good
Еще одни скобки необходимы, поскольку & имеет более высокий
приоритет, чем |.