Помогите разобраться с заданием

200
28 ноября 2018, 17:20

Вообщем вот задание

Составить программу, которая будет записывать контейнер целых чисел в файл (текстовый или бинарный) читать целые числа из файла (текстового или бинарного) в контейнер Запись контейнера в файл (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";
}
Answer 1

Так как реализация метода 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;
}

Стоит заметить, что данное решение очень негибкое. По-хорошему, в файл предварительно необходимо записать количество элементов. Но т.к. решение (по-видимому) используеться в образовательных целях, этими моментами можно пренебречь.

READ ALSO
Есть альтернатива size(), только для массивов?

Есть альтернатива size(), только для массивов?

В функции array_print параметр arr является указателем на элемент массиваУзнать размер массива по указателю на его элемент невозможно

190
Возврат объекта из функции

Возврат объекта из функции

Вопрос по поводу вот такого случая:

190
Получение значения из шейдера

Получение значения из шейдера

Пусть имеется простые шейдера, образующие шейдерную программу:

173
C/C++, Visual Studio, Юникод/Многобайтовые кодировки

C/C++, Visual Studio, Юникод/Многобайтовые кодировки

В настройках проекта Visual Studio можно выбирать набор используемых символовОбычно там есть два пункта:

194