Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Результат_2012_02_09.docx
Скачиваний:
6
Добавлен:
20.04.2015
Размер:
593.36 Кб
Скачать

Задача 8. Реализовать сохранение и загрузку пользовательских структур данных с использованием fstream

Часто в решаемых задачах возникает проблема сохранения сложных пользовательких типов данных. Часто данные сохраняют с помощью ofstream, а загружаую с использованием ifstream. Проблема сохранения может быть решена с помощью перегрузки операторов.

Задача: Реализовать сохранение и загрузку множества символов, реализованных в виде пользовательского класса.

Данная задача состоит из двух частей:

  1. Реализация множества в пользовательском классе

  2. Добавление возможности сохранения и загрузки множества в потоки.

Решим первую задачу. Для реализации множества необходимо реализовать операции:

  1. Проверка на существование элемента во множестве

  2. Добавление элемента во множество

  3. Удаление элемента из множества

  1. Для реализации воспользуемся строками с стиле С (т.е. с завершающим нулем). Для проверки существования элемента (т.е. символа) во множестве можно воспользоваться функцией strchr, которая возвращает NULL, если символа в строке нет и не NULL, если он есть (а если быть более точным, то указатель на первое вхождение символа).

  2. Естественно во множестве должна быть строка, которая будет инициализироваться в конструкторе и в которой будем искать символ. Для примера, используем динамическую строку. Поэтому в конструкторе под нее еще будет выделяться память, а в деструкторе – удаляться. В коде эти рассуждения будут реализованы так:

#include <string.h>

class CharSet {

private:

char *data;

public:

CharSet() {

data = new char[1];

data[0] = 0;

}

bool contains(char c) {

return (strchr(data, c) != NULL);

}

~CharSet() {

delete[] data;

}

};

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

bool addChar(char c) {

if (contains(c)) {

return false;

}

int oldLen = strlen(data);

char *newMem = new char[oldLen + 1 + 1];

strcpy(newMem, data);

newMem[oldLen] = c;

newMem[oldLen + 1] = 0;

delete[] data;

data = newMem;

return true;

}

  1. Для удаления символа в строку необходимо проверить, что есть ли он в строке. Если он есть, то надо скопировать данные до него и после него. Реализация этого действия будет выглядить следующим образом:

bool delChar(char c) {

if (contains(c) == false) {

return false;

}

int oldLen = strlen(data);

char *newData = new char[oldLen];

int curPos = 0;

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

if (data[i] == c) {

curPos++;

}

newData[i] = data[curPos++];

}

delete[] data;

data = newData;

}

  1. Теперь перейдем к вопросу о реализации возможности сохранения и загрузки класса в потоках. Для начала решим вопрос о сохранении данных в файл. Для сохранения данных в файл нам нужен поток, в который мы будем сохранять данные и экземпляр класса CharSet. Так же логично предположить, что при сохранении данных (вернее в её реализации) нам нужно знать о внутреннем устройстве CharSet. Следует отметить, что в С++ существуют дружественные функции, которым доступны все области класса (private, public, protected). Дружественную функцию необходимо объявить в классе, а реализовать вне класса.

class CharSet {

private:

char *data;

public:

friend ofstream& operator<<(ofstream &stream, const CharSet &set);

};

ofstream& operator<<(ofstream &stream, const CharSet &set) {

stream << set.data << "\n";

return stream;

}

  1. Следует обратить внимание на то, что функция возвращает ofstream &. Это необходимо для того, что бы можно было писать «temp << value1 << value2». Сама реализация функции довольно простая. Теперь перейдем к вопросу реализации функции загрузки. Так как это пример демонстрационный, воспользуемся методом получения строки из потока и затем преобразуем её в строку в стиле С.

ifstream& operator>>(ifstream &stream, CharSet &set) {

string loadedStr;

getline(stream, loadedStr);

set.clear();

for (int i = 0; i < loadedStr.size(); i++) {

set.addChar(loadedStr[i]);

}

return stream;

}

  1. Приведем полный код программы:

#include <string.h>

#include <fstream>

#include <string>

using namespace std;

class CharSet {

private:

char *data;

public:

friend ofstream& operator<<(ofstream &stream, const CharSet &set);

friend ifstream& operator>>(ifstream &stream, CharSet &set);

CharSet() {

data = new char[1];

data[0] = 0;

}

virtual ~CharSet() {

delete[] data;

}

void clear() {

data[0] = 0;

}

bool addChar(char c) {

if (contains(c)) {

return false;

}

int oldLen = strlen(data);

char *newMem = new char[oldLen + 1 + 1];

strcpy(newMem, data);

newMem[oldLen] = c;

newMem[oldLen + 1] = 0;

delete[] data;

data = newMem;

return true;

}

bool delChar(char c) {

if (contains(c) == false) {

return false;

}

int oldLen = strlen(data);

char *newData = new char[oldLen];

int curPos = 0;

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

if (data[i] == c) {

curPos++;

}

newData[i] = data[curPos++];

}

delete[] data;

data = newData;

}

bool contains(char c) {

return (strchr(data, c) != NULL);

}

};

ofstream& operator<<(ofstream &stream, const CharSet &set) {

stream << set.data << "\n";

return stream;

}

ifstream& operator>>(ifstream &stream, CharSet &set) {

string loadedStr;

getline(stream, loadedStr);

set.clear();

for (int i = 0; i < loadedStr.size(); i++) {

set.addChar(loadedStr[i]);

}

return stream;

}

int main(int argc, char* argv[]) {

CharSet z;

for (char i = 'a'; i < 'h'; i++) {

z.addChar(i);

}

ofstream outFile("data.txt");

outFile << z;

outFile.close();

z.clear();

ifstream inFile("data.txt");

inFile >> z;

inFile.close();

return 0;

}