Візуальне програмування та MFC - Збереження та відновлення стану об’єктів

Процес виконання такого завдання частково вже розглядався в лекціях, присвячених одновіконному та багатовіконному додаткам. Такі програми, підготовлені за допомогою AppWizard, використовують цей механізм за допомогою методів класуCDocument. Програмістові пропонується лише перевизначити методSerializeцього класу для роботи з конкретними даними програми.

Програміст може визначити свій клас (на основі базового класуCObject) для роботи з даними і скористатися механізмом запису і відновлення об'єктів, що розглядається нижче.

Створення класу, що забезпечує серіалізацію даних

Бібліотека класів MFC визначає механізм запису та відновлення об'єктів (serialization), причому підтримка цього механізму здійснюється засобами класуCObject.

Макрокоманду DECLARE_SERIAL необхідно помістити в описі класу у файлі. Безпосередньо після цієї макрокоманди слід вказати ім'я класу

Перед визначенням класу у файлі вихідного тексту програми слід вказати Макрокоманду IMPLEMENT_SERIAL. Прототип макрокоманди IMPLEMENT_SERIAL представлений нижче:

IMPLEMENT_SERIAL (ім'я_класу, ім'я_базового_класу, номер_версії)

Параметрім'я_класувизначає ім'я класу,ім'я_базового_класу- ім'я базового класу, з якого безпосередньо успадковується клас. Останній параметрномер_версії- це число типу UINT, що визначає версію програми. Якщо розробляється нова версія програми та змінюється набір даних, які необхідно записати у файл, потрібно змінити значенняномер_версії.

У класі мають бути визначені спеціальні методи для запису та відновленнястану об'єктів цього. Зазвичай ці методи зберігають та відновлюють елементи даних із класу. Таким чином, об'єкти класу самі відповідають за те, як вони зберігають та відновлюють свій стан.

Методи, що зберігають та відновлюють об'єктів, взаємодіють з об'єктом класуCArchive, який здійснює безпосередній запис та читання інформації з файлу на диску.

КласCObjectмістить віртуальний методSerialize, що відповідає за запис та читання об'єктів класів, успадкованих від класуCObject:

virtual void Serialize (CArchive & ar);

Як параметр ar методу передається покажчик на об'єкт класуCArchive, що використовується для запису та відновлення стану об'єкта класуCObject(або класу, що успадковується від нього). Щоб дізнатися, яку операцію має виконати методSerialize, необхідно скористатися методамиIsLoadingабоIsStoringкласуCArchive.

Отже, при створенні нового класу, в якому методSerializeзастосовується для серіалізації даних, необхідно:

Наведемо шаблон (заготівлю) файлів визначення та реалізації класу, який забезпечує процес серіалізації даних:

CMyDoc(); // Інші описи класу. >; // Фрагмент файлу реалізації класу IMPLEMENT_SERIAL(CMyDoc, CObject,1) CMyDoc::CMyDoc() < // тут можливе динамічне створення об'єктів та // ініціалізація змінних, якщо це необхідно ЏЏЏ >

Механізм запису та відновлення об'єктів

Розглянемо пропоновану MFC модель обміну даними між документом та файлом, в якому цей процес йде через проміжнийоб'єкт-архівкласуCArchiveбібліотеки MFC. Цей об'єкт передається методуSerializeкласудокумент як параметр. Тому програміст посилає дані над файл, а архів і читає з архіву.

З архіву дані передаються до файлу або читаються з файлу. Об'єкт-архів завжди пов'язаний із файлом (об'єктом класуCFile). Діалог при відкритті та збереженні документа дозволяє задати ім'я фізичного файлу, що зберігає дані документа. Потім створюється об'єкт класуCFile, який пов'язується з архівом. Зауважимо, що об'єкт-архів, що створюється автоматично, існує на час виконання тільки однієї операції - читання документа або запису документа, після чого руйнується.

Розглянемо процес запису та відновлення документа для одновіконної або багатовіконної програми, що створюється за допомогою AppWizard. При виборі команд Open, Save, SaveAsкаркас програми:

  • Відображає відповідне діалогове вікно для отримання імені файлу від користувача.
  • Відкриває файл, вказаний користувачем як об'єкт класуCFile. Створює об'єкт-архів класуCArchive, пов'язуючи його з файлом. Об'єкт-архів отримує статус "store" або "load", залежно від того, чи будуть дані документа записуватися або відновлюватися з архіву.
  • Викликає методSerialize, визначений у класі документа програми, похідному від класуCDocument, використовуючи методиWriteObjectабоReadObjectдля об'єкта-архіву.
  • Визначений розробником програми методSerializeкласу документа запише значення змінних документа в архів. Через буфер архіву дані передаються файл, пов'язаний з об'єктом-архівом.
  • Завершивши збереження документа, каркас програми зруйнує створений об'єкт-архів, а потім об'єкт-файл.

У цьому процесі розробник програмипокладається єдиний обов'язок - визначити методSerializeкласу документа. Він відповідає лише за те, які змінні документи та як записуватимуться до архіву.

Якщо програміст сам організує процес серіалізації даних документа, він сам повинен виконати всі перелічені вище дії. Допустимо, програма виводить вікно, яке має меню, що містить пункти "Store" та "Load". Клас вікна тоді має приблизно такий опис:

При виборі користувачем з меню "Store" викликається метод-обробник OnStore, а при виборі пункту "Load" - метод-обробник OnLoad. Наведемо приклади організації процесу збереження та відновлення документа.

Методи класу CArchive

Перед тим, як створити об'єкт-архів класуCArchive, необхідно створити об'єкт класуCFile. Зв'язуючи з об'єктомCFileфайл на диску, потрібно мати на увазі, що якщо потрібно записати дані, то файл потрібно відкривати на запис, а якщо рахувати - то для читання.

Конструктор класуCArchiveмає такий вигляд:

CArchive(CFile* pFile, UINT nMode, int nBufSize=512, void* lpBuf=NULL);

Параметр pFile повинен містити вказівник на об'єкт класуCFile, з якого зчитуватимуться або записуватимуться дані. Перед викликом конструктора файл, пов'язаний з об'єктом pFile, має бути відкрито.

Параметр nMode визначає, чи дані записуватимуться у файл або зчитуватимуться з нього: CArchive::load (дані будуть зчитуватися з файлу на диску), CArchive::store (дані записуватимуться у файл на диску).

Для операцій читання та запису у файл класCArchiveвиконує буферизацію. Розмір буфера визначається необов'язковим параметром nBufSize. За замовчуванням використовується буфер розміром 512 байт.

Конструктор класуCArchiveсам отримує операційну систему блок оперативної пам'яті для буфера. Однак, за допомогою необов'язкового параметра lpBuf програміст може надати йому власний буфер. У цьому випадку параметр nBufSize повинен вказувати розмір буфера.

Створений об'єкт класуCArchiveможна використовувати тільки для запису або читання. Якщо необхідно спочатку рахувати дані, а потім записати, слід створити для цього два окремі об'єкти класу CArhive.

Не можна використовувати методи класуCFileдля доступу до файлу під час його використання спільно з об'єктами класуCArchive. Запис, читання або переміщення вказівника файлу може спричинити порушення у внутрішній структурі файлу архіву.

Після використання методівCArchive::ReadObjectіCArchive::WriteObject для відновлення або запису об'єктів, необхідно закрити об'єкт класуCArchive, що використовується для цього. Для цього потрібно викликати методCarchive::Close.

Після виклику цього методу потрібно закрити файл, пов'язаний з об'єктомCarchive, викликавши методCFile::Close, і видалити сам об'єкт класуCFile.

Запис до архівного файлу

Коли програма бажає зберегти стан об'єкта у файлі, він викликає для нього методSerialize. Як параметр цього методу передається покажчик на об'єкт класуCArhive, пов'язаний із файлом, відкритим на запис.

Для того, щоб визначити, чи об'єкт-архів призначений для запису або для читання, можна викликати методиCArchive::IsLoadingабоCArchive::IsStoring.

МетодIsStoringповертає ненульове значення, якщо даний об'єкт призначений для запису у файл, і нуль інакше. МетодIsLoadingє прямою протилежністю методу IsStoring . Можна використовувати будь-який метод визначення режиму роботи з об'єктом-архівом.

У разі запису в архівний файл реалізація методу повинна зберегти у файлі всі елементи даних, які потім буде потрібно відновити. Для цього необхідно скористатися оператором>або методамиReadStringтаRead, визначеними в класіCArchive.

Дані з архівного файлу повинні зчитуватися в тому самому порядку, в якому вони були записані.

Оператор >> можна використовувати для читання з архівного файлу змінних простих типів, наприклад, long, int, char і об'єктів інших класів, успадкованих від класу CObject.

Для читання з архівного файлу масивів, записаних у нього методом Write, треба використовувати методReadкласуCArchive. Він дозволяє прочитати з файлу певну кількість байт та записати його у вказаний буфер.

Якщо потрібно прочитати з архівного файлу рядок, записаний у нього методомWriteString, потрібно скористатися методомReadString. До складу класуCArchiveвходять два методиReadString, які призначені для запису з файлу рядка в об'єкт класуCStringабо у звичайний рядок.