Слайд 1Классы ввода-вывода являются важнейшими классами стандартной библиотеки С++, - программа,
которая не вводит и не выводит данные, вряд ли принесет
много пользы.
Классы ввода-вывода стандартной библиотеки С++ не ограничиваются операциями с файлами, экраном и клавиатурой. Они создают расширяемую архитектуру для форматирования произвольных данных и работы с произвольными внешними представлениями.
Библиотека IOStream (как называется совокупность классов ввода-вывода) единственная часть стандартной библиотеки С++, которая широко использовалась до стандартизации С++.
В ранние поставки систем С++ включались классы, разработанные в АТ&Т и ставшие фактическим стандартом ввода-вывода. Хотя в дальнейшем эти классы адаптировались для интеграции со стандартной библиотекой С++ и выполнения некоторых новых функций, базовые принципы, заложенные в классы библиотеки IOStream, остались неизменными.
Классы ввода-вывода на С++
Слайд 2Основные принципы потоковых классов ввода-вывода остались неизменными, но были добавлены
некоторые важные новшества, расширяющие возможности настройки и адаптации. В частности:
Классы
библиотеки IOStream преобразованы в шаблоны, что позволяет поддерживать разные представления символов.
Потоковые классы для символьных массивов типа char* были заменены классами, использующими строковые типы стандартной библиотеки С++. Старые классы по-прежнему поддерживаются для обеспечения совместимости, но они считаются устаревшими!
Обработка ошибок была интегрирована с обработкой исключений.
Все символические имена библиотеки IOStream, как и в остальных компонентах стандартной библиотеки С++, объявляются в пространстве имен std.
Классы ввода-вывода на С++ - новое
Слайд 3В С++ операции ввода-вывода выполняются при помощи потоков данных.
Согласно принципам
объектно-ориентированного программирования, поток данных предcтaвляeт собой объект, свойства которого определяются
классом.
Вывод интерпретируется как запись данных в поток, а ввод - как чтение данных из потока.
Для стандартных каналов ввода-вывода существуют стандартные глобальные объекты.
Специализированные разновидности ввода-вывода (ввод, вывод, операции с файлами) представлены в библиотеке разными классами. Среди потоковых классов центральное место занимают следующие:
класс istream - входной поток, используемый для чтения данных;
класс ostream - выходной поток, используемый для записи данных.
Оба класса представляют собой специализации шаблонов basic_istream<> и basic ostream<> для типа символов char. Библиотека IOStream не зависит от конкретного типа символов - для большинства классов библиотеки этот тип передается в аргументе шаблона.
Потоки
Слайд 4Одним из аргументов в пользу потоков является простота использования: нет
необходимости задумываться о форматировании – каждый объект сам знает, как
он должен выглядеть. Это избавляет программиста от одного из основных источников ошибок.
Другим преимуществом является возможность перегрузки операторов и функций вставки и извлечения потоковых данных при работе с собственными классами. Это позволяет работать с ними как со стандартными типами, что опять же делает программирование проще, не говоря уже о сокращении возможных ошибок и удобстве работы.
Но нужны ли потоки в условиях работы в графической среде, подобной Windows?
Оказывается, что да. Потому что это лучший способ записывать данные в файл, лучший способ организации данных в памяти для последующего использования при вводе-выводе текста в окошках и других элементах графического интерфейса среды.
Потоки - преимущества
Слайд 5В библиотеке IOStream определено несколько глобальных объектов типов istream и
ostream. Эти объекты соответствуют стандартным каналам ввода-вывода.
Объект cin (класс istream)
представляет стандартный входной канал, используемый для ввода пользовательских данных. Он соответствует потоку данных stdin в языке С. Обычно операционная система связывает этот канал с клавиатурой.
Объект cout (класс ostream) представляет стандартный выходной канал, предназначенный для вывода результатов работы программы. Он соответствует потоку данных stdout в языке С. Обычно операционная система связывает этот канал с монитором.
Объект cerr (класс ostream) представляет стандартный канал, предназна-ченный для вывода всевозможных сообщений об ошибках. Он соответствует потоку данных stderr в языке С. Обычно операционная система также связы-вает этот канал с монитором. По умолчанию вывод в сеrr не буферизуется.
Объект clog (класс ostream) представляет стандартный канал для регистрации данных и не имеет аналогов в языке С. По умолчанию этот поток данных связывается с тем же приемником, что и cerr, но вывод в clog буферизуется.
Глобальные потоковые объекты
Слайд 6Операторы сдвига >> и
и означают соответственно ввод и вывод. При помощи этих операторов
можно выполнять каскадные операции ввода-вывода. Например, следующий цикл при каждой итерации читает из стандартного входного потока данных два целых числа (пока вводятся только целые числа) и записывает их в стандартный выходной поток данных:
int а, b:
// Пока операции ввода а и b проходят успешно
while (std::cin >> а >> b {
// Вывод а и b
std::cout << "а: " << а << "b: " << b << std::endl;
Потоковые операторы
Слайд 7В конце большинства команд потокового ввода-вывода записывается так называемый манипулятор:
std::cout
Часто манипуляторы изменяют только режим интерпретации ввода или форматирования вывода (например, манипуляторы выбора системы счисления dec, hex и oct). Это означает, что манипуляторы потока данных ostream не всегда создают выходные данные, а манипуляторы потока данных istream не всегда интерпретируют ввод. Однако некоторые манипуляторы выполняют непосредственные действия - очистку выходного буфера, переключение в режим игнорирования пропусков при вводе и т. д.
Манипулятор endl обозначает -«конец строки», а при его выводе выполняются
две операции.
1. Отправка признака новой строки (то есть символа \n) в выходной поток данных.
2. Очистка выходного буфера (принудительный вывод всех буферизованных данных методом «flush».
Манипуляторы
Слайд 8В классах потокового ввода-вывода istream и ostream определен (кроме рассмотренного
endl) следующий набор манипуляторов:
Манипуляторы - 2
Кроме перечисленных существуют и другие,
в частности используемые для проведения форматирования потока
Слайд 9Программа читает два вещественных числа и выводит их произведение.
Простой пример
int
f()
{
double х, у;
// Операнды
cout << "Multiplication of two floating point values" << endl; // Вывод заголовка
cout << "first operand: "; // Чтение первого операнда
if (! (cin>> х)) { /* Ошибка ввода => вывести сообщение *
* и завершить программу с кодом ошибки */
cerr << "error while reading the first floating value" << endl;
return EXIT_FAILURE;
}
cout << "second operand: "; // Чтение второго операнда
if (! (cin>> у)) { /* Ошибка ввода => вывести сообщение *
* и завершить программу с кодом ошибки */
cerr << "error while reading the second floating value"<< endl;
return EXIT_FAILURE;
}
cout<<х<<" times "<<у<<" equals "<<х * у<< endl; // Вывод операндов и результата
return EXIT_SUCCESS;
}
Слайд 10Для шаблонных классов в верхней строке указано имя шаблона,
а в
нижней - имена специализаций для типов char и wchar_t
Иерархия основных
потоковых классов
ios_base
basic_streambuf<>
streambuf / wstreambuf
basic_ios<>
ios / wios
basic_istream<> istrеаm / wistream
basic_ostream<> ostrеаm / wostream
basic_iostream<>
iostream / wiostream
(virtual)
Слайд 11Базовый класс ios_base определяет свойства всех потоковых классов, не зависящие
от типа и трактовок символов. Класс в основном состоит из
компонентов и функций, предназначенных для управления состоянием и флагами формата.
Шаблон класса basic_ios<>, производный от ios_base, определяет общие свойства всех потоковых классов, зависящие от типа и трактовок символов. В число этих свойств входит определение буфера, используемого потоком данных. Буфер представлен объектом класса, производным от базового класса basic_streambuf<>, с соответствующей специализацией. Фактически именно он выполняет операции чтения/записи.
Назначение основных потоковых классов
Слайд 12Шаблоны basic_istream и basic_ostream, виртуально производные от basic_ios< >, определяют
объекты, которые могут использоваться соответственно для чтения и записи. Эти
классы, как и basic_ios< >, оформлены в виде шаблонов, параметризованных по типу и трактовкам символов. Если проблемы интернационализации несущественны, задействуются специализации этих классов для типа символов char (а именно istream и ostream).
Шаблон basic_iostream<> является производным от двух шаблонов – basic_istream<> и basic_ostream<>. Он определяет объекты, которые могут использоваться как для чтения, так и для записи.
Шаблон basic_streambuf<> занимает центральное место в библиотеке IOStream. Он определяет интерфейс всех представлений, записываемых в потоки данных или читаемых из потоков данных, и используется другими потоковыми классами для фактического чтения или записи символов. Для получения доступа к некоторым внешним представлениям классы объявляются производными от basic_streambuf< >.
Назначение основных потоковых классов - 2
Слайд 13Назначение потоковых буферных классов
Библиотека IOStream предполагает строгое разделение обязанностей.
Классы, производные
от basic_ios, ограничиваются форматированием данных. Операции чтения и записи символов
выполняются потоковыми буферами, которые представлены объектами, подчиненными по отношению к классу basic_ios. Потоковые буферы обеспечивают выполнение чтения/записи в символьных буферах и помогают абстрагироваться от внешнего представления (например, файлов или строковых данных).
Потоковые буферы играют важную роль при выполнении ввода-вывода с новыми внешними представлениями (например, сокетами или компонентами графического интерфейса), перенаправлении потоков данных или их конвейерном объединении (например, при сжатии выходных данных перед их передачей в другой поток данных). Кроме того, потоковые буферы обеспечивают синхронизацию при одновременном вводе-выводе с одним внешним представлением.
Потоковые буферы упрощают определение новых «внешних представле-ний» (скажем, предназначенных для работы с новым носителем данных). Для этого требуется лишь объявить новый потоковый буферный класс, производный от basic_streambuf<> (или его подходящей специализации) и определить функции чтения и/или записи символов для нового внешнего представления.
Слайд 14Заголовочные файлы
Определения потоковых классов распределены по нескольким заголовочным файлам.
. Содержит
опережающие объявления потоковых классов. Этот заголовочный файл необходим из-за того,
что простые опережающие объявления вида class ostream теперь не разрешены.
. Содержит определения базового потоков ого класса с буферизацией (basic_streambuf<>).
. Содержит определения классов, поддерживающих только ввод (basic_istream<>, а также классов с поддержкой ввода и вывода (basic_iostream<>).
. Содержит определения потокового класса вывода (basic_ostream<>).
. Содержит объявления глобальных потоковых объектов (таких, как cin и cout).
Многие из этих заголовочных файлов предназначены для внутренней организации стандартной библиотеки С++. Прикладному программисту обычно достаточно включить файл в объявление потоковых классов и или при непосредственном использовании функций ввода или вывода.
Слайд 15Заголовочные файлы -2
Заголовок следует включать только при использовании стандартных
потоковых объектов. В некоторых реализациях в начале работы каждой единицы
трансляции, включающей этот заголовок, выполняется фрагмент кода инициализации.
Само по себе выполнение этого кода обходится недорого, но при этом приходится загружать соответствующие страницы исполняемого файла, а эта операция может быть довольно дорогостоящей. Как правило, в программу следует включать только заголовки, содержащие абсолютно необходимые объявления. В частности, в заголовочные файлы должен включаться только заголовок , а соответствующие файлы реализации включают заголовок с полным определением.
Специальные средства работы с потоками данных (параметризованные манипуляторы, файловые и строковые потоки данных) определяются в дополнительных заголовочных файлах , , и . На первый взгляд кажется, что объявлять классы с поддержкой ввода и вывода в заголовке нелогично. Но так как в начале работы каждой единицы трансляции, включающей , тратится время на инициализацию, объявления для ввода и вывода были выделены в файл .
Слайд 16Состояние потока данных - константы
Общее состояние потока данных определяется несколькими
флагами, представленными константами типа iostate. Тип iostate определяется в классе
ios_base. Конкретный тип констант зависит от реализации (иначе говоря, стандарт не указывает, является тип iostate перечислением, определением целочисленного типа или специализацией класса bitset).
Бит goodbit по определению равен 0. Таким образом, установка флага goodbit означает, что все остальные биты также равны 0. Имя goodbit выбрано не совсем удачно, поскольку нормальное состояние потока данных обозначается не установкой отдельного бита, а сбросом всех остальных битов.
Слайд 17Состояние потока данных – константы 2
Основное отличие флагов failbit и
badbit состоит в том, что последний обозначает более серьезную ошибку.
failbit
устанавливается в том случае, если операция завершилась неудачно, но состояние потока данных позволяет продолжить работу. Обычно этот флаг устанавливается при ошибках форматирования в процессе чтения - например, если программа пытается прочитать целое число, а следующий символ является буквой.
badbit указывает на неработоспособность потока данных или потерю данных, например, при установке указателя в файловом потоке данных перед началом файла.
eofbit обычно устанавливается вместе с failbit, поскольку признак конца файла проверяется и обнаруживается при попытке чтения за концом файла. После чтения последнего символа флаг eofbit не устанавливается, он устанавливается вместе с флагом failbit при следующей попытке чтения символа.
Константы флагов определяются не глобально, а в классе ios_base. Это означает, что они всегда должны использоваться с указанием области видимости или с конкретным объектом. Пример: std::ios_bаsе::eofbit. Или от производных классов: std::ios::eofbit.
Потоковые буферы состояния не имеют, они могут использоваться несколькими потоками.
Слайд 18Функции для работы с состоянием потока данных
Основное отличие флагов failbit
и badbit состоит в том, что последний обозначает более серьезную
ошибку.
Слайд 19Пример работы с функциями
Как установить и сбросить флаг failbit:
// Проверка установки флага failbit
if (strm.rdstate() & std::ios::failbit)
{
std::cout << "failbit was set" << std::endl;
// Сброс только флага failbit
strm.clear (strm.rdstate() & ~std::i0s::failbit);
}
Здесь используются поразрядные операторы & и ~. Оператор ~ возвращает поразрядное дополнение своего аргумента. Следовательно, показанное ниже выражение возвращает временное значение, в котором установлены все биты, кроме failbit: ~ios::failbit.
Оператор & возвращает результат поразрядного пересечения своих операндов. В результате операции устанавливаются только биты, установленные в обоих операндах. При поразрядном объединении всех текущих установленных флагов (rdstate()) со всеми установленными битами, кроме failbit, бит failbit сбрасывается, а значения всех остальных битов сохраняются.
Слайд 20Класс istream
Класс istream предназначен для извлечения данных из потока
ввода. К этому классу относится объект cin.
Для этого класса перегружен
оператор сдвига >>, который извлекает из потока ввода данные любого стандартного типа, разделенные пробельными символами (к ним относятся пробелы, знаки табуляции, конца строки, конца страницы). Кроме того в нем определены следующие функции для чтения последовательностей символов:
Слайд 21Класс istream - функции ввода символа
int istream::get ()
1)
Читает следующий символ.
2) Возвращает прочитанный символ или EОF.
3)
В общем случае возвращаемое значение относится к типу traits::int_type, а EОF величина, возвращаемая при вызове traits::eof(). Для istream это соответственно тип int и константа EOF. Следовательно, для istream эта функция соответствует функциям getchar() и getc() языка С.
4) Возвращаемое значение не обязательно относится к типу символов потока данных; оно также может относиться к типу с более широким диапазоном значений. Без этого было бы невозможно отличить EОF от символа с соответствующим значением.
istream& istream::get (char& с)
1) Присваивает следующий символ аргументу с.
2) Возвращает объект потока данных, по состоянию которого можно проверить, успешно ли выполнено чтение.
Слайд 22Класс istream - функции get
istream& istream::get (char* str, streamsize count)
istream&
istream::get (char* str, streamsize count, char delim)
Обе формы читают до
count-1 символов в строку str.
Первая форма завершает чтение, если следующий читаемый символ является символом новой строки соответствующей кодировки. Для istream это символ \n.
Вторая форма завершает чтение, если следующий читаемый символ является разделителем delim.
Обе формы возвращают объект потока данных, по состоянию которого можно проверить, успешно ли выполнено чтение.
Завершающий символ (delim) не читается.
Символ завершения строки прерывает чтение.
Перед вызовом необходимо убедиться в том, что размер str достаточен для хранения count символов.
Слайд 23Класс istream - функции getline и read
istream& istream::getline (char* str,
streamsize count)
istream& istream::getline (char* str, streamsize count, char delim)
Обе формы
идентичны предыдущим функциям get() со следующими исключениями:
чтение завершается не перед символом новой строки или delim соответственно, а включая этот символ, то есть символ новой строки или delim будет прочитан, если он встречается среди count-1 символов, но он не сохраняется в str,
если прочитанная строка содержит более count-1 символов, функции устанавливают флаг failbit.
istream& istream::read (char* str, streamsize count)
Читает count символов в строку str.
Возвращает объект потока данных, по состоянию которого можно проверить, успешно ли выполнено чтение.
Строка str не завершается автоматически символом завершения строки.
Перед вызовом необходимо убедиться в том, что размер str достаточен для хранения count символов.
Обнаружение признака конца файла в процессе чтения считается ошибкой, для которой устанавливается бит failbit (вдобавок к флагу eofbit).
Слайд 24Класс istream - функции readsome и gcount
streamsize istream::readsome (char* str,
streamsize count)
Читает до count символов в строку str.
Возвращает
количество прочитанных символов.
Строка str не завершается автоматически символом завершения строки.
Перед вызовом необходимо убедиться в том, что размер str достаточен для хранения count символов.
В отличие от функции read() функция readsome() читает из потокового буфера все доступные символы (при помощи функции in_avail() класса буфера).
Например, она может использоваться в ситуациях, когда ввод поступает с клавиатуры или от других процессов, поэтому ожидание нежелательно. Обнаружение конца файла не считается ошибкой, а биты eofbit и failbit не устанавливаются
streamsize istream::gcount () const
Возвращает количество символов, прочитанных последней операцией неформатированного ввода.
Слайд 25Класс istream - функции ignore
istream& istream::ignore ()
istream& istream::ignore (streamsize count)
istream&
istream::ignore (streamsize count, int delim)
Все формы извлекают символы из
потока данных и игнорируют их.
Первая форма игнорирует один символ.
Вторая форма игнорирует до count символов.
Третья форма игнорирует до count символов и прекращает работу тогда, когда будет извлечен и проигнорирован символ delim.
Если значение count равно std::numeric_limits::max(), то есть максимальному значению типа std::streamsize, функция игнорирует все символы до тех пор, пока не будет обнаружен ограничитель delim или конец файла.
Все формы возвращают объект потока данных.
Примеры:
// игнорирование остатка строки:
cin.ignore(numeric_limits::mах(), '\n');
// игнорирование всех оставшихся символов cin:
cin.ignore(numeric_limits::mах());
Слайд 26Класс istream - функции unget, putback и peek
istream& istream::unget ()
istream&
istream::putback (char с)
Обе функции возвращают в поток данных последний
считанный символ, чтобы он был считан следующей операцией чтения (если не изменится позиция ввода).
Различия между функциями unget() и putback() заключаются в том, что putback() проверяет, был ли передаваемый символ с последним считанным символом.
Если символ не удается вернуть или функция рutbасk() пытается вернуть другой символ, устанавливается флаг badbit, что может привести к выдаче соответствующего исключения.
Максимальное количество символов, которые могут быть возвращены в поток данных этими функциями, зависит от реализации. По стандарту гарантированно работает только один вызов этих функций между двумя операциями чтения.
int istream: :peek ()
Возвращает следующий считываемый из потока данных символ без его извлечения. Символ считывается следующей операцией чтения (если не изменится позиция ввода).
Если дальнейшее чтение невозможно, возвращает ЕОF.
ЕОF - значение, возвращаемое traits::еоf().
Слайд 27Класс istream - функции - резюме
При чтении С-строк описанные здесь
функции безопаснее оператора >>, поскольку они требуют явной передачи максимального
размера читаемой строки.
Хотя количество читаемых символов можно ограничить и при использовании оператора >>, об этом часто забывают.
Вместо использования потоковых функций ввода часто удобнее работать с потоковым буфером напрямую. Функции потоковых буферов позволяют эффективно читать отдельные символы или последовательности символов без затрат на конструирование объектов sentry.
Также можно воспользоваться шаблонным классом istreambuf_iterator, предоставляющим итераторный интерфейс к потоковому буферу.
Функции tellg() и seekg() предназначены для изменения текущей позиции чтения. В основном они используются при работе с файлами.
Слайд 28Класс ostream – функции
ostream& ostream::put (char с)
Записывает аргумент c
в поток данных.
Возвращает объект потока данных, по состоянию которого
можно проверить, успешно ли выполнена запись.
ostream& ostream::write (const char* str, streamsize count)
Записывает count символов строки str в поток данных.
Возвращает объект потока данных, по состоянию которого можно проверить, успешно ли выполнена запись.
Символ завершения строки не останавливает запись и выводится вместе с остальными символами.
Перед вызовом необходимо убедиться в том, что str содержит не менее count символов, иначе вызов приводит к непредсказуемым последствиям.
ostream& ostream::flush ()
Очищает потоковые буферы (принудительная запись всех буферизованных данных на устройство или в канал ввода-вывода, с которым связан буфер).
Слайд 29Пример
Классический фильтр - вывод в выходной поток все прочитанные символы
:
#include
using namespace std;
int main()
{ char с;
while (cin.get(c)) {
// Пока удается прочитать символ
cout.put(с); // Вывести прочитанный символ в выходной поток
}
При каждом вызове cin.get(c) следующий символ присваивается переменной с, которая передается по ссылке. Функция get() возвращает объект потока данных; таким образом, условие while остается истинным до тех пор, пока поток данных находится в нормальном состоянии.
Чтобы повысить эффективность программы, можно работать напрямую с потоковыми буферами. Такой интерфейс лучше классического интерфейса фильтров в языке С. В С вам пришлось бы использовать функцию getchar() или getc(), которая возвращает как следующий символ, так и признак конца файла. В этом случае возвращаемое значение пришлось бы обрабатывать как тип int, чтобы отличить символ от признака конца файла.
Слайд 30Тесты …
Вопрос: Что будет выведено на экран?
#include
class A
{
int count;
public:
A() : count(0) { }
A(A & a) : count(a.count+1) {}
A & operator = (A & a) {
return count *= 10, *this;
}
void print() { std::cout << count; }
};
int main(int argc, char * argv[]) {
A a, b = a, c = b = b, d = c = c = c;
c.print();
}
Варианты ответа:
1101
Ошибка компиляции
(3) 1100
(4) 100
Слайд 31Тесты …
Вопрос: Что будет выведено на экран?
#include
class A
{
int count;
public:
A() : count(0) { }
A(A & a) : count(a.count+1) {}
A & operator = (A & a) {
return count *= 10, *this;
}
void print() { std::cout << count; }
};
int main(int argc, char * argv[]) {
A a, b = a, c = b = b, d = c = c = c;
c.print();
}
Варианты ответа:
1101
Ошибка компиляции
(3) 1100
(4) 100
Создание "а", конструктор по умолчанию, a.count = 0.
Создание "b", конструктор копирования, b.count = a.count + 1 = 1.
Создание "с" надо рассматривать как c = (b = b), т.е. до инициализации "с" надо вычислить выражение в правой части оператора = :
сначала срабатывает оператор присваивания b = b, после чего b.count = 10. А затем уже конструктор копирования для с = b (с новым count), т.е. c.count = b.count + 1 = 10 + 1 = 11.
Создание "d". По аналогии с пред. пунктом сначала вычисляется с = с = с, т.е. два оператора присваивания, где получаем c.count = c.count*10*10 = 11*10*10 = 1100. "d" можно дальше не вычислять.
Слайд 32Тесты …
Что будет выведено на консоль в результате выполнения программы?
#include
struct A {
char foo() { return 'A';}
};
template
T> struct B : public T {
virtual char foo() {return 'B';}
};
template struct C : public T {
virtual char foo() {return 'C';}
};
int main(int argc, char* argv[])
{
A* a = new A();
A* b = new B< A >();
A* c = new C< A >();
A* d = new B< C< A > >();
std::cout << a->foo() << b->foo() << c->foo() << d->foo();
return 0;
}
Слайд 33Тесты …
Что будет выведено на консоль в результате выполнения программы?
#include
struct A {
char foo() { return 'A';}
};
template
T> struct B : public T {
virtual char foo() {return 'B';}
};
template
struct C : public T {
virtual char foo() {return 'C';}
};
int main(int argc, char* argv[])
{
A* a = new A();
A* b = new B< A >();
A* c = new C< A >();
A* d = new B< C< A > >();
std::cout << a->foo() << b->foo() << c->foo() << d->foo();
return 0;
}
AAAA
Параметры шаблонов B и C определяют родителя генерируемого класса.
Так как метод foo() базового класса (класса A) невиртуальный, то в данном случае позднее связывание использовано не будет и во всех случаях будет вызван метод foo() класса A.
Слайд 34Тесты …
Вопрос: В каких строках (укажите номера) допущены ошибки:
class
cl
{
static int x;
public :
static int y;
static void
f2();
void f1();
};
int cl::x=1;
int cl::y=2;
void cl::f1()
{ x++; y++; } // 1
void cl::f2()
{ x++; y++; } //2
void f3()
{ cl::x++; cl::y++; } // 3
Слайд 35Тесты …
Вопрос: В каких строках (укажите номера) допущены ошибки:
class
cl
{
static int x;
public :
static int y;
static void
f2();
void f1();
};
int cl::x=1;
int cl::y=2;
void cl::f1()
{ x++; y++; } // 1
void cl::f2()
{ x++; y++; } //2
void f3()
{ cl::x++; cl::y++; } // 3
в 3 т.к функция вне класса имеет доступ только к public переменным класса.