Вообщем вот задание
Составить программу, которая будет
записывать контейнер целых чисел в файл (текстовый или бинарный)
читать целые числа из файла (текстового или бинарного) в контейнер
Запись контейнера в файл (template method
) должен содержать последовательность следующих операций
открытие файла
запись чисел
закрытие файла
Метод считывания (template method
) из файла должен содержать последовательность следующих операций
открытие файла
Считывания чисел и добавления их в контейнер
закрытие файла
Использовать паттерн Шаблонный метод.
Тут проблема в том что запись и чтение 2 разные типа(ofstream
и fstream
).И если в интерфейсе сделать один метод то потом при наследовании нельзя будет выбрать ofstream или ifstream а если сделать методы разные то тогда толку от этого паттерна.
Вот код:
class Base
{
virtual void open(ofstream &a) = 0;
virtual void doing(array<int, 6>::iterator it, ofstream &a) = 0;
void close(ofstream &a)
{
a.close();
}
public:
void execute( array<int, 6>::iterator it, ofstream &a)
{
open(a);
doing(it,a);
close(a);
}
};
class Writer:public Base
{
virtual void open(ofstream &a)
{
a.open("FIle.bin", ios::binary);
if (!a)
cout << "Error\n";
}
virtual void doing( array<int, 6>::iterator it, ofstream &a)
{
a.write((char*)&it, sizeof(it));
}
};
class Reader:public Base
{
virtual void open(ofstream &a)
{
a.open("FIle.bin", ios::binary);
if (!a)
cout << "Error\n";
}
virtual void doing(array<int, 6>::iterator it, ofstream &a)
{
a.read((char*)&it, sizeof(it));
}
};
void main()
{
array<int, 6> ll {0,1,2,3,4,5};
Base *arr[]{ &Writer(),&Reader() };
ofstream f1;
ifstream f2;
arr[1]->execute(ll.begin(),f2);
arr[0]->execute(ll.begin(), f1);
for (int i = 0; i < 6; i++)
cout << ll[i]<<" ";
cout << "\n\n";
}
Так как реализация метода doing
может выполнять как запись так и чтение передача ссылки на std::ofstream
не годится. Стандартная библиотека предоставляет класс std::fstream
, который является слиянием std::ifstream
и std::ofstream
, то что нужно.
Далее, семантика методов doing
(из Writer
-а):
a.write((char*)&it, sizeof(it));
Этот код не выполняет то, что вы предполагаете. Вы берете адресс итератора it
, что не имеет никакого отношения к данным, на которые указывает итератор. Нужно записывать не адресс итератора, а данные, которые он хранит.
Итератор это хорошо, но вам нужна работа с последовательностью. Должен быть передан итератор на начало и на конец последовательности.
Т.к. std::fstream
работает как с потоками ввода, так и с потоками вывода, вам необходимо явно указывать для каких целей открывается поток: для ввода (std::ios::in
) или для вывода (std::ios::out
).
Base *arr[]{ &Writer(),&Reader() };
В данном отрезке кода: создается объект типа Writer
(все сказанное также относится к Reader
). Выполняется операция взятия адресса, адресс записывается в массив arr
и затем объект, адресс которого мы запомнили уничтожается. В результате получим висячий указатель.
Один из возможных корректных вариантов:
Writer writer{};
Reader reader{};
Base* arr[] = { &writer, &reader };
Корректная реализация:
Base class
class Base
{
virtual void open(std::fstream &a) = 0;
virtual void doing(array<int, 6>::iterator first, // итератор на начало
array<int, 6>::iterator last, // итератор на конец
std::fstream& stream // ссылка на поток
) = 0;
void close(std::fstream &a)
{
a.close();
}
public:
// те же изменения
void execute(array<int, 6>::iterator first,
array<int, 6>::iterator last,
std::fstream &a)
{
open(a);
doing(first, last, a);
close(a);
}
};
Writer class
class Writer : public Base
{
virtual void open(std::fstream &a) override
{
a.open("FIle.bin", ios::binary | std::ios::out);
if (!a)
cout << "Error\n";
}
virtual void doing(array<int, 6>::iterator first,
array<int, 6>::iterator last, std::fstream& stream) override
{
// проверка на доступность потока
// проверка на валидность итераторов
for (; first != last; ++first)
{
// получаем указатель на реальные данные которые хранит итератор
const char* pointer_to_data = reinterpret_cast<const char*>(&*first);
// записываем эти данные
// sizeof(int), sizeof(*first),
// sizeof(std::array<int, 6>::value_type) : как угодно
stream.write(pointer_to_data, sizeof(*first));
}
}
};
Reader class
class Reader : public Base
{
virtual void open(std::fstream &a) override
{
a.open("FIle.bin", ios::binary | ios::in);
if (!a)
cout << "Error\n";
}
virtual void doing(array<int, 6>::iterator first,
array<int, 6>::iterator last, std::fstream& stream) override
{
// проверка на доступность потока
// проверка на валидность итераторов
for (; first != last; ++first)
{
// проверка активен ли поток и может ли быть произведено чтение
// получаем указатель на место памяти, куда указывает итератор
char* pointer_to_data = reinterpret_cast<char*>(&*first);
stream.read(pointer_to_data, sizeof(*first));
}
}
};
Еще, в вашем примере, вы сначала производите операцию чтения, а затем операцию записи. Проверить за один заход работу можно следующим образом:
int main()
{
Reader reader{};
Writer writer{};
std::fstream stream{}; // кстати, достаточно одного потока fstream
std::array<int, 6> arr1{ 1, 2, 3, 4, 5, 6 };
std::array<int, 6> arr2;
std::cout << "arr1 до записи, (arr2 пустой): \n";
for (size_t i = 0; i < 6; ++i)
std::cout << arr1[i] << "\t";
std::cout << "\n";
// записываем данные из arr1
writer.execute(arr1.begin(), arr1.end(), stream);
// читаем данные в arr2
reader.execute(arr2.begin(), arr2.end(), stream);
std::cout << "arr2 после чтения: \n";
for (size_t i = 0; i < 6; ++i)
std::cout << arr2[i] << "\t";
std::cout << "\n";
return 0;
}
Стоит заметить, что данное решение очень негибкое. По-хорошему, в файл предварительно необходимо записать количество элементов. Но т.к. решение (по-видимому) используеться в образовательных целях, этими моментами можно пренебречь.
Виртуальный выделенный сервер (VDS) становится отличным выбором
В функции array_print параметр arr является указателем на элемент массиваУзнать размер массива по указателю на его элемент невозможно
В настройках проекта Visual Studio можно выбирать набор используемых символовОбычно там есть два пункта: