Есть 2 класса, один наследуется от другого, причем базовый класс содержит хотя бы 1 виртуальный метод.
При первом запуске программы я записываю в файл в бинарном виде экземпляр наследуемого класса и при повторном запуске программы я пытаюсь считать из файла обратно в объект. Если не использовать полиморфизм, то есть не создавать виртуальные функции в базовом классе, то все работает нормально, в другом случае программа крашится, пишет obj-> было 0x..... ,посмотрев в отладчике я заметил что чтение памяти из _vfptr невозможно, вот и задаюсь вопросом, как это можно обойти.
class B {
public:
B() {}
float f;
virtual void printf() = 0;
};
class A : public B {
public:
int a;
char k;
A() {
f = 2;
a = 1111;
k = '?';
}
void printf() {
cout << a << endl << k << endl << f << endl << endl;
}
};
int main() {
A *obj = new A();
FILE* file;
int t = 1;//(после 1 запуска программы меняю значение на 2 и повторно запускаю)
if (t == 1) {
file = fopen("file.dat", "w+b");
fwrite(obj, sizeof(obj), 1, file);
fclose(file);
}
else if (t == 2) {
file = fopen("file.dat", "r+b");
fread(obj, sizeof(obj), 1, file);
fclose(file);
obj->printf();
}
}
Прямая запись нетривиальных объектов в бинарный файл как "сырых" бинарных данных возможна только в контексте одной программной сессии. Т.е. фактически такой подход жизнеспособен только если вы реализуете некое подобие своего собственного временного "своп файла".
Пытаться использовать такой способ сохранения данных между программными сессиями ("при повторном запуске программы...") обречены на провал.
Чтобы выполнить сохранение, живущее между программными сессиями, вам придется самостоятельно разработать некий формат/протокол (возможно, весьма нетривиальный), который позволит вам сериализовать те свойства ваших данных, которые впрямую в файл записать нельзя: значения указателей, типы полиморфных объектов и т.д. и т.п.
Вещи, которые не являются plain old data, нельзя просто писать на диск и считывать как единое целое. Даже если внутри класса нет указателей на какие-то данные, у вас есть указатель на таблицу виртуальных функций. Пока вы в пределах одного выполнения, указатели еще сохраняются, так что возможно чтение-запись, но в разных...
Заведите функции записи и чтения членов-данных, а не всего объекта в целом.
Вот тут, тут или тут я уже отвечал на подобные вопросы, можете посмотреть.
Вот вам код, как это делать правильно.
class B {
public:
B() {}
float f;
virtual void printf() = 0;
virtual void write(FILE* fl)
{
fwrite(&f,sizeof(f),1,fl);
}
virtual void read(FILE* fl)
{
fread(&f,sizeof(f),1,fl);
}
};
class A : public B {
public:
int a;
char k;
A() {
f = 2;
a = 1111;
k = '?';
}
void printf() {
cout << a << endl << k << endl << f << endl << endl;
}
virtual void write(FILE* fl)
{
B::write(fl);
fwrite(&a,sizeof(a),1,fl);
fwrite(&k,sizeof(k),1,fl);
}
virtual void read(FILE* fl)
{
B::read(fl);
fread(&a,sizeof(a),1,fl);
fread(&k,sizeof(k),1,fl);
}
};
int main() {
A *obj = new A();
FILE* file;
int t = 1;//(после 1 запуска программы меняю значение на 2 и повторно запускаю)
if (t == 1) {
file = fopen("file.dat", "w+b");
obj->write(file);
fclose(file);
}
else if (t == 2) {
file = fopen("file.dat", "r+b");
obj->read(file);
fclose(file);
obj->printf();
}
}
Как любитель нестандартных выстрелов в ногу я не мог пройти мимо. Рабочее решение найти удалось, вот кусок кода:
int main()
{
A *obj = new A();
FILE* file;
int t = 2;//(после 1 запуска программы меняю значение на 2 и повторно запускаю)
if (t == 1)
{
file = fopen("file.dat", "w+b");
// Обратите внимание, в оригинале у вас тут написано sizeof(obj), но obj - это указатель, а его размер всегда 4 (или 8) байт
// надо писать либо sizeof(A) либо sizeof(*obj), чтобы получить реальный размер занятой памяти
fwrite(obj, sizeof(A), 1, file);
fclose(file);
}
else if (t == 2)
{
A* donor = new A(); // Донор нам нужен для копирования __vfptr
A* obj2 = new A(); // Объект, куда читаем из файла
A* obj3 = new A(); // Объект, который будет создан по данным, считанным из файла
memset(obj2, 0, sizeof(A)); // Для чистоты эксперимента вся память зануляется
memset(obj3, 0, sizeof(A));
// Читаем из файла в obj2
file = fopen("file.dat", "r+b");
fread(obj2, sizeof(A), 1, file);
fclose(file);
// После чтения из файла мы получили obj2 с невалидным __vfptr, копируем целиком всю память в obj3
memcpy(obj3, obj2, sizeof(A));
// Копируем для obj3 валидный __vfptr из экземпляра-донора
memcpy(obj3, donor, sizeof(void*));
// И оно работает (по крайней мере у меня)
obj3->printf();
}
getchar();
return 0;
}
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости