Я написал простую программу, чтобы научиться использовать произвольное заполнение. Он компилируется нормально, но во время выполнения выдает ошибку нарушения доступа. Я пишу и читаю только одну запись.
Заголовочный файл:
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
#ifndef HEADER_H
#define HEADER_Hclass info
{
private:
int id;
string name;
public:
info(int = 0, string = " ");
void set(int, string);
void display();
void write();
void read();
};
#endif
Файл реализации:
#include<iostream>
#include<string>
#include<fstream>
#include"Header.h"using namespace std;
info::info(int x, string y)
{
set(x, y);
}
void info::set(int x, string y)
{
id = x;
name = y;
}
void info::display()
{
cout << "\n\n\tid : " << id;
cout << "\n\tname" << name;
}
void info::write()
{
ofstream o("info.dat", ios::out | ios::binary | ios::app);
info a(id, name);
info *p = &a;
o.write(reinterpret_cast<const char *>(p), sizeof(info));
o.close();
cout << "\n\n\tWrite Successful";
}
void info::read()
{
ifstream i("info.dat", ios::in | ios::binary);
i.seekg(0);
info a(0, "a");
info *p = &a;
i.read(reinterpret_cast<char *>(p), sizeof(info));
i.close();
p->display();
cout << "\n\n\tRead Successful";
}
Главный:
#include<iostream>
#include<string>
#include<fstream>
#include"Header.h"using namespace std;
void main()
{
info a(10, "muaaz");
a.write();
a.display();
info b(2, "m");
b.read();
}
Ошибка возникает после функции чтения. Функция cout «Read Successful» в конце функции read работает нормально, и после этого в main нет других операторов. Я не знаю, что является причиной ошибки.
Каждый случай reinterpret_cast
в вашем коде должен быть четкий намек на то, что происходит что-то опасное и подверженное ошибкам.
void info::write() { ofstream o("info.dat", ios::out | ios::binary | ios::app); info a(id, name); info *p = &a; o.write(reinterpret_cast<const char *>(p), sizeof(info)); o.close(); cout << "\n\n\tWrite Successful"; }
Это не может работать правильно. Все вопросы языка юриста в стороне, рассмотрите точку зрения реализации здесь. std::string
обычно непосредственно не содержит (как элемент данных) текст, который он представляет, но вместо этого содержит указатель динамически распределяемой памяти, где хранится текст.
Итак, что вы в итоге делаете, это записываете адрес памяти в файл.
Этого уже достаточно для того, чтобы подход был обречен на любую сериализацию, поскольку очевидно, что адреса памяти различны для разных запусков одной и той же программы.
Но это не сработает даже в этом простом примере, когда файл записывается и читается из одного и того же выполнения. a
это локальный объект. когда write
заканчивается, объект уничтожается, а вместе с ним std::string
это содержит. Когда std::string
уничтожается, память, выделенная для его содержимого, освобождается.
Следовательно, как только write
сделано, ваш файл содержит неверный адрес памяти. Несколько мгновений спустя, в read
функция, вы пытаетесь вставить этот неверный адрес в новый std::string
, К счастью, вы получаете нарушение прав доступа, поэтому замечаете, что что-то не так. Если бы вам не повезло, программа какое-то время продолжала бы производить желаемое поведение, только чтобы потом начать сбой или делать странные вещи на более позднем этапе.
Некоторые вопросы, которые делают ситуацию еще более сложной:
Макет памяти std::string
не определено стандартом C ++. Таким образом, разные компиляторы с разными настройками компилятора будут создавать разные info.dat
файлы, даже если вы скомпилировали их из того же исходного кода C ++.
std::string
может использовать или не использовать Small-String Optimization (SSO), метод, который означает, что для строк с несколькими символами текст является непосредственно хранится в объекте, а не динамически распределяется. Таким образом, вы даже не можете предположить наличие указателя.
С другой стороны, std::string
может содержать даже Больше указатели на динамически выделенную память, указывающие на конец отображаемого текста и на конец выделенной памяти (для capacity
функция-член).
Учитывая все эти важные внутренние сложности, вас не должно удивлять, что ваша попытка обойти или игнорировать их все с помощью простого reinterpret_cast
Запускает неопределенное поведение.
Сериализация и особенно десериализация не POD-типов не легка в C ++[*] и еще не был стандартизирован. Вы можете рассмотреть стороннюю библиотеку. Как всегда, Boost стоит попробовать, когда дело доходит до сторонних библиотек, и, как оказалось, он содержит библиотеку сериализации, а именно Boost.Serialization.
[*] На самом деле, это нелегко ни на одном языке, даже на тех, которые делают это легко, например, на Java с его очень опасным Serializable
интерфейс.
Других решений пока нет …