unique_ptr нарушение доступа для чтения при потере области видимости - PullRequest
1 голос
/ 17 мая 2019

Я читаю массив char из двоичного файла, используя std :: ifstream, затем переосмысливаю массив char в указатель на мою структуру, а затем делаю его unique_ptr. Все работает хорошо, за исключением случаев, когда unique_ptr выходит из области видимости, я получаю нарушение прав чтения.

Я все делаю неправильно? Я не уверен, почему происходит ошибка. У меня есть только один unique_ptr для этих данных.

Я включил базовую версию кода, чтобы вызвать ошибку.

struct mystruct {
    int val = 0;
    double val2 = 5.6;
    std::string string = "test";

};

int main()
{

    //brackets here just to encapsulate the scope for this example
    {
        //try to open the stream for reading
        std::ifstream stream;
        stream.open("test.bin", std::ios::binary | std::ios::in);

        char* buffer = new char[sizeof(mystruct)];
        stream.read(buffer, sizeof(mystruct));

        mystruct * pointer = reinterpret_cast<mystruct*>(buffer);
        std::unique_ptr<mystruct> obj = std::unique_ptr<mystruct>(pointer);

        //ha no problem reading the struct data
        std::cout << "read back: " << obj->string << endl;

        stream.close();
    }

    //obj goes out of scope and throws a read access violation
}

Я ожидаю, что unique_ptr просто удалит объект, и не будет выдано никакой ошибки

********** РЕДАКТИРОВАТЬ ********************
Спасибо за комментарии и ответы - в основном благодаря вашей помощи я создал код, который пытался сделать, и перечислил его здесь на случай, если он кому-нибудь поможет.
Основные пункты были:
* std :: string не рекомендуется в структуре при чтении и записи из двоичного файла, поскольку std :: string имеет неизвестное количество байтов.
* необходимо создать объект в памяти, прежде чем назначить на него указатель - для этого подойдет std :: make_unique ().

struct mystruct {
    int val1 = 0;
    double val2 = 5.6;
    char somestring[10] = "string";

};

int main()
{

    //brackets here just to encapsulate the scope for this example
    {
        //try to open the stream for reading
        std::ifstream stream;
        stream.open("test.bin", std::ios::binary | std::ios::in);

        //hold the results in a vector
        auto results = std::vector<std::unique_ptr<mystruct>>();

        //read a vectory or mystructs from the binary file
        while (!stream.eof())
        {
            //create the object - NOTE: make_unique initialises my struct
            std::unique_ptr<mystruct> obj = std::make_unique<mystruct>();

            //read from binary file into obj
            if (!stream.read(reinterpret_cast<char*>(obj.get()), sizeof mystruct))
                break;

            //add the obj to th vector
            results.push_back(std::move(obj));
        }

        stream.close();

        for (auto& val : results)
        {
            cout << "read back: " << val->somestring << endl;
        }


    }
}

1 Ответ

2 голосов
/ 17 мая 2019

В вашем коде есть 3 вида неопределенного поведения.

Во-первых, вы делаете вид, что есть объект в месте, где его нет. Вы никогда не создавали mystruct; Вы только что выделили несколько байтов памяти. Просто сделать reinterpret_cast недостаточно для создания mystruct. Таким образом, любое использование pointer, которое обращается к «объекту», который не существует, является UB.

Во-вторых, даже если он был в этом буфере, mystruct не может быть просто скопирован, поэтому вы не можете просто скопировать его байты. Вы не можете прочитать кучу байтов в нетривиально копируемый объект. Именно наличие нетривиально копируемого нестатического элемента данных (то есть: mystruct::string) делает его нетривиально копируемым.

И в-третьих, вы пытаетесь удалить это mystruct. Но там нет mystruct, вы удаляете то, что не существует. Технически, это, вероятно, покрыто # 1, но это, вероятно, то, что приводит к полному краху вашего кода.

Если вы знаете, почему «без проблем с чтением данных структуры» сработало, вполне вероятно, что реализация std::string использует оптимизацию небольших строк, которая хранит строку внутри самого std::string, если она достаточно мала , Для небольших строк выполнение побайтной копии, вероятно, достаточно близко к «работе», чтобы вы могли читать строковые данные.

Но это просто повезло.

...