Исключение нулевого указателя - это индикатор того, что вы используете объект, не инициализируя его.
Например, ниже - класс ученика, который будет использовать его в нашем коде.
public class Student {
private int id;
public int getId() {
return this.id;
}
public setId(int newId) {
this.id = newId;
}
}
Приведенный ниже код дает вам исключение с нулевым указателем.
public class School {
Student obj_Student;
public School() {
try {
obj_Student.getId();
}
catch(Exception e) {
System.out.println("Null Pointer ");
}
}
}
Поскольку вы используете Obj_Student
, но вы забыли инициализировать его, как в правильном коде, показанном ниже:
public class School {
Student obj_Student;
public School() {
try {
obj_Student = new Student();
obj_Student.setId(12);
obj_Student.getId();
}
catch(Exception e) {
System.out.println("Null Pointer ");
}
}
}
Boost::serialization
библиотека обрабатывает это скорее изящно. Я использовал его в нескольких проектах. Существует пример программы, показывая, как использовать его, здесь .
единственный собственный способ сделать это состоит в том, чтобы использовать потоки. Это по существу весь Boost::serialization
, библиотека делает, она расширяет потоковый метод путем установки платформы, чтобы записать объекты в подобный тексту формат и считать их из того же формата.
Для встроенных типов или Ваших собственных типов с operator<<
и operator>>
правильно определенный, это довольно просто; см. C++ FAQ для получения дополнительной информации.
Повышение является хорошим предложением. Но если Вы хотели бы к самокрутке, это не настолько твердо.
В основном Вам просто нужен способ создать график объектов и затем произвести их к некоторому формату структурированного хранилища (JSON, XML, YAML, безотносительно). Создание графика так же просто как использование отмечающего рекурсивного достойного объектного алгоритма и затем вывод всех отмеченных объектов.
я написал статью, описывающую элементарное (но все еще мощный) система сериализации. Можно найти это интересным: Используя SQLite как Дисковый Формат файла, Часть 2 .
Насколько "встроенные" библиотеки идут, <<
и >>
были зарезервированы специально для сериализации.
необходимо переопределить <<
для вывода объекта к некоторому контексту сериализации (обычно iostream
) и >>
для чтения данных назад из того контекста. Каждый объект ответственен за вывод его агрегированных дочерних объектов.
Этот метод хорошо работает, пока Ваш граф объектов не содержит циклов.
, Если это делает, затем необходимо будет пользоваться библиотекой для контакта с теми циклами.
Я рекомендую буферы протокола Google . У меня был шанс к тест-драйву библиотека по новому проекту, и это удивительно просто в использовании. Библиотека в большой степени оптимизирована для производительности.
Protobuf отличается, чем другие решения для сериализации упомянули здесь в том смысле, что он не сериализирует Ваши объекты, а скорее генерирует код для объектов, которые являются сериализацией согласно Вашей спецификации.
Я использую следующий шаблон для реализации сериализации:
template <class T, class Mode = void> struct Serializer
{
template <class OutputCharIterator>
static void serializeImpl(const T &object, OutputCharIterator &&it)
{
object.template serializeThis<Mode>(it);
}
template <class InputCharIterator>
static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
{
return T::template deserializeFrom<Mode>(it, end);
}
};
template <class Mode = void, class T, class OutputCharIterator>
void serialize(const T &object, OutputCharIterator &&it)
{
Serializer<T, Mode>::serializeImpl(object, it);
}
template <class T, class Mode = void, class InputCharIterator>
T deserialize(InputCharIterator &&it, InputCharIterator &&end)
{
return Serializer<T, Mode>::deserializeImpl(it, end);
}
template <class Mode = void, class T, class InputCharIterator>
void deserialize(T &result, InputCharIterator &&it, InputCharIterator &&end)
{
result = Serializer<T, Mode>::deserializeImpl(it, end);
}
Здесь T
тип, который Вы хотите сериализировать Mode
, фиктивный тип для дифференциации между различными видами сериализации, например, то же целое число может быть сериализировано как прямой порядок байтов, обратный порядок байтов, varint, и т.д.
По умолчанию Serializer
делегаты задача к сериализируемому объекту. Для созданного в типах необходимо сделать шаблонную специализацию Serializer
.
шаблоны функций Удобства также предоставлены.
, Например, сериализация с прямым порядком байтов целых чисел без знака:
struct LittleEndianMode
{
};
template <class T>
struct Serializer<
T, std::enable_if_t<std::is_unsigned<T>::value, LittleEndianMode>>
{
template <class InputCharIterator>
static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
{
T res = 0;
for (size_t i = 0; i < sizeof(T); i++)
{
if (it == end) break;
res |= static_cast<T>(*it) << (CHAR_BIT * i);
it++;
}
return res;
}
template <class OutputCharIterator>
static void serializeImpl(T number, OutputCharIterator &&it)
{
for (size_t i = 0; i < sizeof(T); i++)
{
*it = (number >> (CHAR_BIT * i)) & 0xFF;
it++;
}
}
};
Затем для сериализации:
std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));
Для десериализации:
uint32_t val;
deserialize(val, serialized.begin(), serialized.end());
из-за абстрактной логики итератора, это должно работать с любым итератором (например, потоковыми итераторами), указатель, и т.д.