Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP / Практика 1.pdf
Скачиваний:
23
Добавлен:
20.04.2015
Размер:
125.24 Кб
Скачать

cln » name;

cout « "Введите возраст: "; cln » age;

}

};

int main ()

{

person pers;

//создать объект pers.getData ();

//получить данные

//создать объект ofstream

ofstream outfile("PERSON.DAT", ios::binary); // записать в него

outfile.write(reinterpret_cast<char*>(&pers), sizeof(pers)); return 0;

};

Метод getData () класса person вызывается для того, чтобы запросить у пользователя информацию, которая помещается в объект pers.

Содержимое данного объекта записывается на диск с помощью функции write (). Для нахождения длины данных объекта pers используется оператор sizeof ().

Чтение объекта с диска

Листинг 12.11. Программа IPERS #include <fstream>

#include <iostream> using namespace std;

//для файловых потоков class person

{

protected : char name[80];

short age; public: void showData ()

//класс person

//Имя человека // его возраст

//вывести данные

cout « "Имя: " « name « endl; cout « "Возраст: " « age « endl; int main ()

{

person pers;

// переменная типа person

ifstream infile (“PERSON. DAT”, ios::binary);// создать поток // чтение потока

infile.read( reinterpret_cast<char*>(&pers), sizeof (pers));

pers.showData ():

// вывести данные

return 0;

 

}

 

В результате работы программы будет выведено на экран все то, что было помещено в файл PERSON.DAT программой OPERS:

Совместимость структур данных

Для корректной работы программы чтения и записи объектов (такие, как OPERS и IPERS) должны иметь в виду один класс объектов. Например, объекты класса person имеют длину ровно 82 байта, из которых 80 отведено под имя человека, 2 — под возраст в формате short. Если бы программы не знали длину полей, то одна из них не смогла бы корректно прочитать из файла то, что записала другая.

Несмотря на то что классы person в OPERS и IPERS имеют одинаковые компонентные данные, у них могут быть совершенно разные методы. Скажем, в первой программе есть функция getData (), тогда как во второй — showData (). He имеет значения, какие используются методы в классах, — они в файл вместе с данными не записываются. Это для данных важен единый формат, а разногласие между методами ни к каким последствиям не приводит. Впрочем, это утверждение справедливо только для обычных классов, в которых не используются виртуальные функции.

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

Можно даже не пытаться произвести дисковый ввод/вывод объектов, компонентами которых являются указатели. Легко догадаться, что значения указателей не будут корректными при чтении объекта в другую область памяти.

Ввод/вывод множества объектов

Предыдущие программы записывали и читали только один объект за раз. В следующем примере в файл записывается произвольное число объектов. Затем они считываются, и на экран выводится целиком содержимое файла.

Листинг 12.12. Программа DISKFUN

#include <fstream>

// для файловых потоков

#include <iostream>

 

using namespace std;

 

class person

 

{

 

 

protected:

 

 

char name[80]:

 

int age:

 

 

public:

 

 

void getData ()

 

{

Введите имя: ";

cout « "\n

cln » name:

Введите возраст: ";

cout « "

cln » age:

 

 

}

 

// вывод данных о человеке

void showData ()

{

Имя: " « name;

cout « "\n

cout « "\n

Возраст: " « age;

}

 

 

int main ()

{

char ch; person pers; fstream file;

file.open («GROUP. DAT», ios::app | ios::out | ios: :in | ios::binary ) do

{

cout « "Введите данные о человеке:"; pers.getData (); // получить данные // записать их в файл

file.write( reinterpret_cast<char*>(&pers). slzeof(pers) ); cout « "Продолжить ввод (y/n)? ";

cln » ch;

}

// выход по 'n1

wh11e(ch=='y' ); flle.seekg(O);

//поставить указатель на начало файла

//считать данные о первом человеке

file.read ( reinterpret_cast<char*>(&pers), slzeof(pers) ); while ( !file.eof () ) // Выход по EOF

{

cout « "\nПерсона:"; //вывести данные

pers.showData (); //считать данные о следующем file.read(reinterpret_cast<char*>(&pers).s1zeof(pers));

}

cout « endl ; return 0;

}

В настоящей, серьезной программе некоторые из этих функций должны использоваться после каждой операции ввода/вывода, и тогда не останется сомнений в том, что все идет как нужно.

Файловый ввод/вывод с помощью методов

Ранее мы сводили все функции работы с файлами в секцию main () наших программ. Но при написании сложных классов логичнее включать операции файлового ввода/вывода в

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

Как объекты записывают и читают сами себя

Иногда имеет смысл разрешить каждому компоненту класса читать и записывать самого себя. Это довольно прозрачный подход и отлично работает, если нужно писать и читать небольшое число объектов. В нашем примере мы добавили два метода — diskOut () и diskln () — в класс person. Эти функции позволяют объектам person записывать себя в файл и читать себя же из него.

Примем некоторые упрощающие допущения. Во-первых, все объекты будут храниться в одном и том же файле PERSFILE.DAT. Во-вторых, новые объекты всегда будут добавляться к концу файла. Аргумент функции diskln () позволяет читать данные о любом человеке из файла. Для предотвращения попыток прочитать данные, выходящие за пределы файла, мы включаем в программу статический метод diskCount (), возвращающий число людей, информация о которых хранится в файле. При вводе данных следует использовать только

фамилии людей, пробелы не допускаются. Листинг 12.16. Программа REWOBJ

//rewobj.cpp

//Файловый ввод/вывод объектов person #include <fstream>

//Для файловых потоков

#include <iostream> using namespace std; class person

{

protected: char name[40]; int age; public:

void getData()

{

cout « "\n Введите фамилию: "; cin » name; cout « " Введите возраст: "; cin » age;

}

void showData()

{

cout « "\nИмя: " « name; cout « "\nВозраст: " « age; void diskln(int);

//чтение из файла void diskOutO;

//запись в файл

static int diskCount ();

// Число человек в файле }:

void person::diskln(int pn) // Чтение данных о числе

{

//человек из файла ifstream infile;

//создать поток

infile.open (“PERSFILE.DAT”, ios::binary); // открыть его infile.seekg( pn*sizeof(person) );

//сдвиг

//файлового указателя

infile.read( (char*)this, sizeof(*this) ); // чтение данных

//об одном человеке

}

void person::diskOut()

{

ofstream outfile;

//запись в конец файла

//создать поток

//открыть его

outfile.open (“PERSFILE.DAT", ios::app | ios::binary); outfile.write((char*)this.sizeof(*this)); //записать в него

}

int person::diskCount()

{

//число людей в файле ifstream infile;

infile.open («PERSFILE.DAT», ios::binary); infile.seekg(0, ios::end); // перейти на позицию «О байт //от конца файла» // вычислить количество людей

return (int) infile.tellg () / sizeof(person);

}

int main ()

{

person p; char ch;

//создать пустую запись

do {

Соседние файлы в папке OOP