2010-07-30 3 views
0

Во-первых, я знаю, что писать класс на диск плохо, но вы должны увидеть некоторые из нашего другого кода. D:Можете ли вы написать полиморфный класс на диск и выжить?

Мой вопрос: могу ли я написать полиморфный класс на диск, а затем прочитать его позже и не получить неопределенное поведение? Я собираюсь угадать не из-за vtables (I думаю они генерируются во время выполнения и уникальны для объекта?)

I.e.

class A { 
    virtual ~A() {} 
    virtual void foo() = 0; 
}; 

class B : public A { 
    virtual ~B() {} 
    virtual void foo() {} 
}; 

A * a = new B; 

fwrite(a, 1, sizeof(B), fp); 

delete a; 

a = new B; 

fread(a, 1, sizeof(B), fp); 

a->foo(); 

delete a; 

Thank you you!

+4

Могу ли я спросить, ПОЧЕМУ вы пытаетесь снять это? Почему вы не можете писать код сериализации для записи и чтения состояния? Если вам нужна эта функция, вы можете захотеть закодировать ее на Java. – Uri

+6

Обычно вы не можете писать ЛЮБОЙ класс (полиморфный или нет) на диск с помощью простого вызова функции fwrite и надеяться снова прочитать его. – 2010-07-30 14:46:38

+0

Я думаю, вам следует искать альтернативный ответ на вашу проблему, а не спрашивать, как делать что-то такое ужасное, как это мой друг :) –

ответ

1

Я предложу вам взглянуть на Boost Serialization.

мы используем термин «сериализация», чтобы означать обратимую деконструкцию произвольного набора структур данных C++ для последовательности байтов. Такая система может быть использована для восстановления эквивалентной структуры в другом программном контексте. В зависимости от контекста, это может использоваться для реализации сохранения объекта, передачи удаленных параметров или другого объекта.

+0

У меня есть другие комментарии, что это невозможно. Было бы очень полезно использовать boost, но я сомневаюсь, что это произойдет – user274244

-1

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

Однако вы должны иметь в виду, что чтение указателей/ручек из файла не работает.

+0

Нет, это не сработает, так как местоположение vtable может измениться. – Artyom

0

Вы могли быть в состоянии уйти с ним, если такие объекты всегда считываются обратно в течение того же выполнения программы, что их написал (хотя я действительно не рекомендую). Но если данные в файле должны сохраняться между различными исполнениями программы, то использование необработанных байтов объектов в памяти почти наверняка приведет к значительным проблемам.

Каждая виртуальная таблица сама генерируется во время компиляции и хранится где-то в результирующем исполняемом файле. Каждый экземпляр объекта содержит только указатель на соответствующую таблицу vtable, и этот указатель не изменяется для срока жизни любого данного объекта. (Множественное наследование может быть немного сложнее, но для этого обсуждения эти детали не актуальны. Указатели по-прежнему постоянны.)

Так что если у объекта есть указатель vtable, и вы записываете необработанные байты этого объекта в диск, то указатель vtable также записывается на диск. Если вы затем зачитаете эти байты во время одного и того же выполнения программы и вставляете их в соответствующий объект, то может работать, так как vtable все равно будет находиться в одном месте и, таким образом, указатель vtable будет по-прежнему корректным.

(Однако обратите внимание, что все, что я только что объяснил, есть деталь реализации. Хотя многие компиляторы обычно реализуют виртуальные функции таким образом, я не думаю, что какая-либо конкретная информация гарантирована стандартом C++. Так что может являются дополнительными потенциальными проблемами.)

Теперь, если это возможно, почему бы не хранить такие объекты в течение более длительных периодов времени? Поскольку у вас нет гарантии, что какая-либо конкретная виртуальная таблица будет находиться в одном месте памяти.

Некоторые операционные системы могут выполнять change the memory layout для каждого выполнения одной и той же программы. Я не знаю, действительно ли это влияет на расположение виртуальных таблиц, но это, безусловно, серьезный риск.

Кроме того, если вы когда-либо компилируете новую версию программы, расположение каждой виртуальной таблицы полностью зависит от прихотей компилятора. Изменения в кажущейся несвязанной части кода могут привести к тому, что компилятор разместит соответствующие виртуальные таблицы в разных местах. Очевидно, что это событие полностью нарушит эту схему. И у вас нет способа предотвратить это.

(И помимо переменных vtables, если новые элементы данных должны быть добавлены к этим объектам в последующих версиях программы? Возможно, вам придется иметь дело с чтением прошлых версий байтов необработанных объектов в новые версии, в которых есть новые члены или другая компоновка элементов. Это может усложниться и некрасиво, а также к ошибкам.)


Теперь, даже если вы только собираетесь хранить объекты временно для каждого выполнения программы. Я все еще не думаю, что это хорошая идея. Вы сильно ограничены в отношении того, какие переменные могут содержать эти объекты. Нет интеллектуальных объектов (std :: string, std :: vector и т. Д.). Никаких указателей на память, выделенных для каждого объекта. Поэтому любые строки должны храниться в необработанных массивах символов. Другое динамическое распределение должно быть превращено в фиксированные элементы или массивы-члены. Это означает, что вы теряете много преимуществ C++ везде, где используются эти объекты.

Кроме того, эти объекты и схема написания этого непосредственно на диске должны сопровождаться комментариями и документацией, предупреждающими обо всех описанных выше опасностях. В противном случае, какой-то будущий программист может неосознанно решить добавить не тот тип данных. Или, что еще хуже, они могут решить попытаться сохранить такие объекты дольше, чем выполнение программы, открыв их для серьезных сбоев и сбоев, которые могут не произойти до тех пор, пока это произойдет намного позже и, возможно, в самое худшее время.


В конце концов, я настоятельно рекомендую использовать схему, которая хранит данные в формате, специально предназначенном для файла. Как уже упоминалось ранее, Boost Serialization - хороший вариант. Если это не так, могут быть другие используемые библиотеки сериализации. Или, в зависимости от ваших потребностей, вы можете свернуть свой механизм без особых проблем.

Смежные вопросы