С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 30
Текст из файла (страница 30)
Например, следующее определение инициализирует// эквивалентно bitvec3string bitva1( "1010" );bitvec4 тем же набором значений, что и bitvec3:bitset< 32 > bitvec4( bitval );Можно также указать диапазон символов строки, выступающих как начальные значения// подстрока с шестой позиции длиной 4: 1010string bitval ( "1111110101100011010101" );для битового вектора. Например:bitset< 32 > bitvec5( bitval, 6, 4 );Мы получаем то же значение, что и для bitvec3 и bitvec4. Если опустить третий// подстрока с шестой позиции до конца строки: 1010101string bitva1( "1111110101100011010101" );параметр, подстрока берется до конца исходной строки:bitset< 32 > bitvec6( bitval, 6 );Класс bitset предоставляет две функции-члена для преобразования объекта bitset вдругой тип.
Для трансформации в строку, состоящую из символов нулей и единиц,служит функция to_string():string bitva1( bitvec3.to_string() );Вторая функция, to_long(), преобразует битовый вектор в его целочисленноепредставление в виде unsigned long, если, конечно, оно помещается в unsigned long.Это видоизменение особенно полезно, если мы хотим передать битовый вектор функциина С или С++, не пользующейся стандартной библиотекой.К объектам типа bitset можно применять побитовые операции. Например:bitset<32> bitvec7 = bitvec2 & bitvec3;Объект bitvec7 инициализируется результатом побитового И двух битовых векторовbitvec2 и bitvec3.166С++ для начинающихbitset<32> bitvec8 = bitvec2 | bitvec3;Здесь bitvec8 инициализируется результатом побитового ИЛИ векторов bitvec2 иbitvec3. Точно так же поддерживаются и составные операции присваивания и сдвига.Упражнение 4.15Допущены ли ошибки в приведенных определениях битовых векторов?(a) bitset<64> bitvec(32);(b) bitset<32> bv( 1010101 );(c) string bstr; cin >> bstr; bitset<8>bv( bstr );(d) bitset<32> bv; bitset<16> bvl6( bv );Упражнение 4.16extern void bitstring(const char*);bool bit_on (unsigned long, int);bitset<32> bitvec;(a) bitsting( bitvec.to_string().c_str() );(b) if ( bit_on( bitvec.to_1ong(), 64 )) ...Допущены ли ошибки в следующих операциях с битовыми векторами?(c) bitvec.f1ip( bitvec.count() );Упражнение 4.17Дана последовательность: 1,2,3,5,8,13,21.
Каким образом можно инициализироватьобъект bitset<32> для ее представления? Как присвоить значения для представленияэтой последовательности пустому битовому вектору? Напишите вариант инициализациии вариант с присваиванием значения каждому биту.4.13. ПриоритетыПриоритеты операций задают последовательность вычислений в сложном выражении.Например, какое значение получит ival?int ival = 6 + 3 * 4 / 2 + 2;Если вычислять операции слева направо, получится 20.
Среди других возможныхрезультатов будут 9, 14 и 36. Правильный ответ: 14.В С++ умножение и деление имеют более высокий приоритет, чем сложение, поэтому онибудут вычислены раньше. Их собственные приоритеты равны, поэтому умножение иделение будут вычисляться слева направо. Таким образом, порядок вычисления данноговыражения таков:1.2.3.4.3 * 4 => 1212 / 2 => 66 + 6 => 1212 + 2 => 14167С++ для начинающихСледующая конструкция ведет себя не так, как можно было бы ожидать. Приоритетоперации присваивания меньше, чем операции сравнения:while ( ch = nextChar() != '\n' )Программист хотел присвоить переменной ch значение, а затем проверить, равно ли оносимволу новой строки.
Однако на самом деле выражение сначала сравнивает значение,полученное от nextChar(), с '\n', и результат – true или false – присваиваетпеременной ch.Приоритеты операций можно изменить с помощью скобок. Выражения в скобкахвычисляются в первую очередь. Например:4 * 5 + 7 * 2 ==> 344 * ( 5 + 7 * 2 ) ==> 764 * ( (5 + 7) * 2 ) ==> 96Вот как с помощью скобок исправить поведение предыдущего примера:while ( (ch = nextChar()) != '\n' )Операторы обладают и приоритетом, и ассоциативностью.
Оператор присваиванияправоассоциативен, поэтому вычисляется справа налево:ival = jval = kva1 = lvalСначала kval получает значение lval, затем jval – значение результата этогоприсваивания, и в конце концов ival получает значение jval.Арифметические операции, наоборот, левоассоциативны. Следовательно, в выраженииival + jval + kva1 + 1va1сначала складываются ival и jval, потом к результату прибавляется kval, а затем иlval.В таблице 4.4 приведен полный список операторов С++ в порядке уменьшения ихприоритета. Операторы внутри одной секции таблицы имеют равные приоритеты.
Всеоператоры некоторой секции имеют более высокий приоритет, чем операторы из секций,следующих за ней. Так, операции умножения и деления имеют одинаковый приоритет, ион выше приоритета любой из операций сравнения.Упражнение 4.18(a) ! ptr == ptr->next(b) ~ uc ^ 0377 & ui << 4Каков порядок вычисления следующих выражений? При ответе используйте таблицу 4.4.(c) ch = buf[ bp++ ] != '\n'Упражнение 4.19168С++ для начинающих169Все три выражения из предыдущего упражнения вычисляются не в тойпоследовательности, какую, по-видимому, хотел задать программист. Расставьте скобкитак, чтобы реализовать его первоначальный замысел.Упражнение 4.20Следующие выражения вызывают ошибку компиляции из-за неправильно понятого(a) int i = doSomething(), 0;приоритета операций.
Объясните, как их исправить, используя таблицу 4.4.(b) cout << ival % 2 ? "odd" : "even";Таблица 4.4. Приоритеты операцийОператорЗначениеИспользование::Глобальнаявидимости::Областькласса::Областьвидимости namespace::nameпространства имен.Доступ к члену->Доступ куказателю[]Взятие индексаvariable[expr]()Вызов функцииname(expr_list)()Построение значенияtype(expr_list)++постфиксный инкремент lvalue++--постфиксный декремент lvalue--typeidидентификатор типаtypeidидентификаторвыраженияconst_castпреобразование типаconst_cast<type>(expr)dynamic_castпреобразование типаdynamic_cast<type>(expr)reinterpret_castприведение типаreinterpret_cast<type>(expr)static_castприведение типаstatic_cast<type>(expr)sizeofразмер объектаsizeof exprsizeofразмер типаsizeof( type)++префиксный инкремент ++lvalue--префиксный декрементобласть ::nameвидимости class::nameobject.memberчленупо pointer->membertypeid(type)типа typeid(expr)--lvalueС++ для начинающих170~побитовое НЕ~expr!логическое НЕ!expr-унарный минус-expr+унарный плюс+expr*разыменование*expr&адрес&expr()приведение типа(type)exprnewвыделение памятиnew typenewвыделение памятиинициализацияnewвыделение памятиnewвыделение памяти под все формымассивdeleteосвобождение памятиdeleteосвобождениеиз-под массива->*доступ к члену классу pointer->*pointer_to_memberпо указателю.*доступ к члену класса object.*pointer_to_memberпо указателю*умножениеexpr * expr/делениеexpr / expr%деление по модулюexpr % expr+сложениеexpr + expr-вычитаниеexpr - expr<<сдвиг влевоexpr << expr>>сдвиг вправоexpr >> expr<меньшеexpr < expr<=меньше или равноexpr <= expr>большеexpr > expr>=больше или равноexpr >= expr==равноexpr == expr!=не равноexpr != expr&побитовое Иexpr & expr^побитовоеИСКЛЮЧАЮЩЕЕexpr ^ exprи new type(exprlist)newtype(exprlist)(exprlist)все формыпамяти все формыС++ для начинающих171ИЛИ|побитовое ИЛИexpr | expr&&логическое Иexpr && expr||логическое ИЛИexpr || expr?:условный операторexpr ? expr * expr=присваиваниеl-значение = expr=, *=, /=, %=, составное присваивание l-значение += expr и т.д.+=, -=, <<=, >>=,&=, |=, ^=throw,возбуждениеисключенияthrow exprзапятаяexpr, expr4.14.
Преобразования типовint ival = 0;// обычно компилируется с предупреждениемПредставим себе следующий оператор присваивания:ival = 3.541 + 3;В результате ival получит значение 6. Вот что происходит: мы складываем литералыразных типов – 3.541 типа double и 3 типа int. C++ не может непосредственно сложитьподобные операнды, сначала ему нужно привести их к одному типу. Для этогосуществуют правила преобразования арифметических типов.
Общий принцип таков:перейти от операнда меньшего типа к большему, чтобы не потерять точностьвычислений.В нашем случае целое значение 3 трансформируется в тип double, и только после этогопроизводится сложение. Такое преобразование выполняется независимо от желанияпрограммиста, поэтому оно получило название неявного преобразования типов.Результат сложения двух чисел типа double тоже имеет тип double. Значение равно6.541. Теперь его нужно присвоить переменной ival. Типы переменной и результата6.541 не совпадают, следовательно, тип этого значения приводится к типу переменнойслева от знака равенства.
В нашем случае это int. Преобразование double в intпроизводится автоматически, отбрасыванием дробной части (а не округлением). Такимобразом, 6.541 превращается в 6, и этот результат присваивается переменной ival.Поскольку при таком преобразовании может быть потеряна точность, большинствокомпиляторов выдают предупреждение.Так как компилятор не округляет числа при преобразовании double в int, приdouble dva1 = 8.6;int iva1 = 5;необходимости мы должны позаботиться об этом сами. Например:С++ для начинающих172ival += dva1 + 0.5; // преобразование с округлением// инструкция компилятору привести double к intПри желании мы можем произвести явное преобразование типов:ival = static_cast< int >( 3.541 ) + 3;В этом примере мы явно даем указание компилятору привести величину 3.541 к типуint, а не следовать правилам по умолчанию.В этом разделе мы детально обсудим вопросы и неявного (как в первом примере), иявного преобразования типов (как во втором).4.14.1.