Прочитать файл, используя C++ и RAII

Какой лучший способ прочитать файл с использованием C++ и RAII? Все примеры, которые я видел, используют что-то похожее на код ниже:

#include <iostream>
#include <fstream>

int main () {

  std::ifstream is ("test.txt", std::ifstream::binary);
  if (is) {
    is.seekg (0, is.end);
    int length = is.tellg();
    is.seekg (0, is.beg);

    char * buffer = new char [length]; // Seems wrong?

    is.read (buffer, length);

    delete[] buffer;
  }
}

Согласно тому, что я знаю о RAII, кажется неправильным инициализировать указатель на символ и удалить его вручную.

Я сделал что-то похожее на:

#include <iostream>
#include <fstream>

int main () {

  std::ifstream is ("test.txt", std::ifstream::binary);
  if (is) {
    is.seekg (0, is.end);
    int length = is.tellg();
    is.seekg (0, is.beg);

    std::shared_ptr<char> buffer = std::make_shared<char>();

    is.read (buffer.get(), length);
  }
}

Но я тоже не уверен, правильно ли это. Я также не смог успешно разыграть std::shared_ptr<char> к std::shared_ptr<uint8_t> если нужно (или если это возможно, или если это вообще имеет смысл?).

2 ответа

Решение

символ * буфер = новый символ [длина]; // Кажется неправильным?

Это не так, просто... небезопасно.

Более безопасные реализации:

std::unique_ptr<char[]> buffer{new char [length]}; // note: use char[] as parameter
is.read (buffer.get(), length);

Лучше, чем предыдущий:

std::vector<char> buffer{length, ' '};
is.read (buffer.data(), length);

или же:

std::string buffer{length, ' '};
is.read (buffer.data(), length);

В частности, это UB:

std::shared_ptr<char> buffer = std::make_shared<char>();
is.read (buffer, length);

потому что он выделяет один символ динамически, а затем помещает до длинных символов в этом месте памяти. На практике это переполнение буфера, если только ваша длина не равна 1.

std::shared_ptr (как и другие умные указатели) применяет RAII к одному экземпляру объекта (здесь один символ, упс!)

Вы ищете std::vector, который вы можете предварительно выделить, вызвав vec.reserve().

Что касается приведения его к uint8_t, я бы сказал, что вызов vec.data() после того, как вы прочитали свои данные и приведение результата к uint8_t, должен работать как положено (хотя я не буду утверждать, что это стандартно).

Другие вопросы по тегам