Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Тема 12. Файловый ввод-вывод

Поиск

 

Файловый и консольный ввод-вывод очень близко связаны. Фактически файловый ввод-вывод поддерживается той же иерархией классов, что и консольный ввод-вывод.

Когда программа начинает работу, четыре потока инициализируются автоматически и связываются с соответствующими им стандартными файлами (устройствами) – это cin, cout, cerr и clog. Если нужно инициализировать еще и другие потоки и связать их с некоторыми файлами, то сделать это следует явно.

Любой инициализируемый файловый поток может быть лиьо потоком ввода, либо потоком вывода, либо потоком ввода-вывода. Поток ввода – это всегда объект класса istream, порожденного из класса istream. Поток вывода – это всегда объект класса ofstream, порожденного из класса ostream. Поток ввода-вывода – это всегда объект класса fstream, порожденного из класса iostream. В каждом из этих классов имеются конструкторы, выполняющие работу по связыванию объекта класса с соответствующим файлом. Классы для работы с файлами наследуют от потоковых классов перегруженные операции “<<” и “>>”, флаги и методы форматирования, манипуляторы, средства проверки состояния потоков и т.п.

Использование файлов в программе предполагает следующие действия:

1. Создание потока.

2. Открытие потока и связывание его с файлом.

3. Обмен с потоком (ввод-вывод).

4. Закрытие файла.

5. Уничтожение потока.

Конструкторы без параметров создают объект не связывая его с файлом. Конструкторы с параметрами создают объект, открывают файл с указанным именем в заданном режиме и связывают файл с объектом. Если установленное по умолчанию значение режима открытия файла не устраивает программиста, то можно указать другое, составив его из битовых масок, определенных в классе ios, с помощью битовой операции “|”.

После создания потока, одним из способом связать его с файлом является функция open( ), которая является членом всех файловых потоковых классов. Если выполнение функции open() завершилось с ошибкой, в булевом выражении поток будет равен значению false.

Для закрытия файла используется функция close() не имеющая параметров и возвращаемого значения.

Рассмотрим один практически значимый пример иллюстрирующий такие важные аспекты программирования, как: потоковый ввод-вывод, использование шаблонов классов при передаче параметров из конструктора производного класса в конструктор базового класса, работа с двумерным массивом, размещаемым в динамической памяти. В силу ограниченности объема лекции в приводимой программе рассматриваются только характерные особенности отмеченных аспектов, а все виды контроля опущены.

// определение класса IOFile для открытия/закрытия файлов

class IOFile: public fstream

{ public:

void open_f(char *filename, int mode) { open (filename, mode); }

void close_f (char *filename) { close ();}

};

 

 

// шаблон базовых классов для работы с матрицами

 

template <class T> // T – обобщенный тип элемента

class Matrix_B // Класс – «Матрица» - базовый класс

{ protected:

T ** array; // указатель на массив указателей на строки

int rows, columns;

public: Matrix_B (int n, int m); // конструктор

Matrix_B (const Matrix_B<T>&copy); // конструктор копирования

~Matrix_B (void); // деструктор

void ReadMatrix (char *fp); // чтение элементов матрицы из файла

};

 

template<class T>Matrix_B<T>:: Matrix_B (int n, int m)

{ array= new T *[n]; // динамическое выделение памяти под матрицу

for (int i=0; i<n; i++) array[i]= new T [m];

for (i=0; i<n; i++)

for (int j=0; j<m; j++) array[i][j]=(T) 0;

rows=n; columns=m;

return;

}

 

 

// конструктор копирования

template<class T>Matrix_B<T>::Matrix_B (const Matrix_B<T> & copy)

{ array= new T *[copy.rows];

for (int i=0; i<copy.rows; i++) array[i]= new T [copy.columns];

for (i=0; i<copy.rows; i++)

for (int j =0; j<copy.columns; j++) array[i][j]=copy.array[i][j];

rows=copy.rows;

columns=copy.columns;

}

 

template<class T>Matrix_B<T>::~Matrix_B (void) // деструктор

{ for (int i=0; i<rows; i++) delete [] array[i];

delete [] array;

}

 

 

// чтение элементов матрицы из заданного файла

template<class T> void Matrix_B<T>::ReadMatrix (char *fp)

{ IOFile fin;

fin.open_f (fp, ios::in);

for (int i=0; i<rows; i++)

for (int j =0; j<columns; j++)

fin>>array[i][j];

fin.close_f(fp);

return;

}

 

// Определение шаблона производных классов для работы с матрицами

template <class T>

class Matrix_D: public Matrix_B<T>

{ public:

Matrix_D (int nrow, int ncol);

void PrintMatrix (IOFile &); // Печать матрицы

// Перегрузка операций сложения, присваивания и индексации

Matrix_ D<T> operator + (const Matrix_D<T>&);

Matrix_ D<T>& operator =(const Matrix_D<T>&);

T *& operator [] (int index);

// Конструктор производного класса

template <class T>

Matrix_D <T>::Matrix_D (int nrow, int ncol): Matrix_B<T>(nrow,ncol) {}

 

// Перегрузка операции сложения

template<class T>

Matrix_D<T> Matrix_D<T>:: operator + (const Matrix_D<T>&ref)

{ Matrix_D<T> result(rows, columns); // Создание пустого объекта

for (int i=0; i<rows; i++)

for (int j=0; j<columns; j++)

result.array[i][j]=array[i][j]+ref.array[i][j];

return result;

}

 

// Перегрузка операции присваивания

template<class T>

Matrix_D<T>& Matrix_D<T>:: operator =(const Matrix_D<T>&ref)

{

for (int i=0; i<rows; i++)

for (int j=0; j<columns; j++)

array[i][j]=ref.array[i][j];

rows=ref.rows;

columns=ref.columns;

return * this;

}

 

// Печать матрицы

template<class T>

void Matrix_D<T>::PrintMatrix (IOFile & fout)

{ for (int i=0; i<rows; i++)

{ for (int j=0; j<columns; j++)

fout << array[i][j]<<’ ‘;

fout <<endl;

}

return;

}

 

// Перегрузка операции индексации

template <class T>

T *& Matrix_D<T>:: operator [] (int index)

{ return array[index]; }

 

// Тестирование программы

 

int main (void) // main возвращает 0 при успехе

{ IOFile fout; // объект для записи в файл “matrix.out”

fout.open_f (“matrix.out”, ios::out);

// Создаем объекты-массивы, заполняем их из файла и печатаем

Matrix_D < int > a1(2,2), a2(2,2), a3(2,2);

a1.ReadMatrix (“matrix.in”);

a2.ReadMatrix(“matrix.in”);

a3.ReadMatrix(“matrix.in”);

fout<<”\n a1: “<< endl; a1.PrintMatrix(fout);

fout<<”\n a2: “<< endl; a2.PrintMatrix(fout);

fout<<”\n a3: “<< end l; a3.PrintMatrix(fout);

 

// Тестирование перегруженной операции []

fout<<”\n Тестирование перегруженной операции []:”<<”\n int k=a1[1][1];”<< endl;

int k=a1[1][1];

fout<<”\n k=”<<k<< endl;

 

// Тестирование элементарных выражений

fout<<”\n Тестирование элементарных выражений \n”<<”a3=a1+a2;”<< endl;

a3=a1+a2;

fout<<”\n a3=”<< endl;

a3.PrintMatrix(fout);

// Закрытие файла вывода

fout.close_f (“matrix.out”);

return 0;

}

 

Основная литература – 5 [Гл. 11, 431–444], 6 [Гл. 5, 135–158], 7 [Гл. 9, 243–275],

8 [Гл.9, 275–302].

 

Вопросы контроля

1. Зачем создавать потоки ввода-вывода, если отлично работают семейства функций scanf()-fscanf(), printf()- fprintf()-sprinf()?

2. Когда следует применять метод ignore ()?

3. Какая ширина поля вывода принимается по умолчанию при выводе целых чисел?

4. Какое значение возвращает перегруженный оператор вывода?

5. Что обеспечивает элементарный режим открытия ios::ate?



Поделиться:


Последнее изменение этой страницы: 2016-12-13; просмотров: 169; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.221.93.167 (0.005 с.)