- •Содержание Введение
- •Задача 1: Обнаружить учетку памяти
- •Задача 2. Составить тесты к программе
- •Задача 3: Напечатать все содержимое файла на экран.
- •Задача 3: Напечатать все содержимое файла на экран (оптимизация).
- •Задача 4. Удалить все нечетные элементы из массива
- •Задача4.Удалить все нечетные элементы из массива (оптимизация)
- •Задача 4. Удалить все нечетные элементы из массива (последующая оптимизация)
- •Задача 4. Удалить все нечетные элементы из массива (основа тестов)
- •Задача 5. В файле удалить все слова, которые начинаются и заканчиваются одной буквой
- •Задача 5. В файле удалить все слова, которые начинаются и заканчиваются одной буквой (без использования потоков и std::string)
- •Задача 6. Удалить строки, в которых есть два одинаковых элемента (без использования std::vector)
- •Задача 6. Удалить строки, в которых есть два одинаковых элемента (c использованием std::vector)
- •Задача 7. Отсортировать содержимое словаря
- •Задача 8. Реализовать сохранение и загрузку пользовательских структур данных с использованием fstream
- •Заключение Глоссарий
- •Список рекомендуемой литературы
Задача 8. Реализовать сохранение и загрузку пользовательских структур данных с использованием fstream
Часто в решаемых задачах возникает проблема сохранения сложных пользовательких типов данных. Часто данные сохраняют с помощью ofstream, а загружаую с использованием ifstream. Проблема сохранения может быть решена с помощью перегрузки операторов.
Задача: Реализовать сохранение и загрузку множества символов, реализованных в виде пользовательского класса.
Данная задача состоит из двух частей:
Реализация множества в пользовательском классе
Добавление возможности сохранения и загрузки множества в потоки.
Решим первую задачу. Для реализации множества необходимо реализовать операции:
Проверка на существование элемента во множестве
Добавление элемента во множество
Удаление элемента из множества
Для реализации воспользуемся строками с стиле С (т.е. с завершающим нулем). Для проверки существования элемента (т.е. символа) во множестве можно воспользоваться функцией strchr, которая возвращает NULL, если символа в строке нет и не NULL, если он есть (а если быть более точным, то указатель на первое вхождение символа).
Естественно во множестве должна быть строка, которая будет инициализироваться в конструкторе и в которой будем искать символ. Для примера, используем динамическую строку. Поэтому в конструкторе под нее еще будет выделяться память, а в деструкторе – удаляться. В коде эти рассуждения будут реализованы так:
#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;
}
};
Для добавления символа в строку необходимо проверить, что его еще нет в строке. Если его нет, то требуется выделить новую память, переписать в нее старые данные, добавить новый символ. И после этого удалить старую память и изменить указатель. Если добавление произошло, то из функции будем возвращать истину. Если не произошло – ложь. В коде эти рассуждения будут реализованы в следующем виде:
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;
}
Теперь перейдем к вопросу о реализации возможности сохранения и загрузки класса в потоках. Для начала решим вопрос о сохранении данных в файл. Для сохранения данных в файл нам нужен поток, в который мы будем сохранять данные и экземпляр класса 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;
}
Следует обратить внимание на то, что функция возвращает 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;
}
Приведем полный код программы:
#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;
}