1

i need a way to serialize objects of different types (but the types deriving from the same class) and then deserialize them to the pointer of the base class, containing the deriving class. For example:

#include<iostream>
#include<fstream>
class One
{
public:
    int a;
    virtual void Func()
    {
    }
};

class One1: public One
{
    char s[128];
    void Func1(int l)
    {
        std::cout<<l<<'\n';
    }
    void Func()
    {
         Func1(156);
    }
};

int main()
{
    One* x = new One1;
    x->Func();
    char* y=(char*)x;
    delete x;
    /*std::ofstream out("test11.txt",std::ofstream::out | std::ofstream::trunc);
    out.write(y,sizeof(One1));
    out.close();
    std::ifstream in("test11.txt",std::ifstream::in);
    char* y1=new char[sizeof(One1)];
    in.read(y1,sizeof(One1));*/
    One* z=(One*)y/*1*/;
    z->Func();
    return 0;
}

This code outputs

156
156

But when I uncomment the comments (when I try to write to a file the char representation of the object and to read from this file then), the program outputs 156 and ends on segmentation fault when trying to execute z->Func();. I checked that the content of the variable y is different from y1. Why?
What is the cause of that issue and how can I address it (maybe by using some special libraries)?

michj36
  • 53
  • 7
  • You can overload the `ofstream` and `ifstream` operators `>>` and `<<` and use those to write to the file. – NathanOliver Mar 16 '15 at 17:49
  • I can suggest good ways to do {de}serialization. I can perhaps explain why the things pointed to by `y` and `y1` differ. I cannot explain exactly why you're getting a segfault except to say that casting pointer types in this manner is dangerous and unnecessary black magic. – Beta Mar 16 '15 at 18:06
  • Firstly, could you explain why the things pointed to by `y` and `y1` differ? Secondly, what are these good ways? – michj36 Mar 16 '15 at 18:09
  • [facepalm] *vtable!*. Anyway, I can't add much to Pawel Stawarz's answer, except to say that I'd make a function that writes a One1's member variables to the of ofstream one by one, and another that reads them from an ifstream and fills in the member variables of a One1. Then I'd make them member functions of One1. Then I'd overload the stream input/output operators (`>>` and `<<`) to use those methods. Testing at every step, of course. – Beta Mar 16 '15 at 19:15
  • But there will be more classess than just the example One1 and I would like to read the object with its type from the input file. I finally found the simplest solution - to numerate the types (with type names such as Type1, Type2...) and to create one function that reads one object from the input. Within that method I used a switch loop with cases generated using boost preprocessor iteration library, so the code is brief. – michj36 Mar 31 '15 at 11:31

1 Answers1

3

1. Don't serialize derived classes by simply copying bytes

You can not simply write polymorphic objects by converting them to a byte array and then load them by a binary read. Classes with virtual functions store pointers to the implementations in a vtable. Dumping an instance of a derived class will result in dumping the pointers stored in the vtable, which - obviouly - doesn't have to be a valid pointer after you run the program once more. Accessing it after that will most probably yield a segmentation fault.

If you really want to use the easy way (directly reading and writing bytes), use POD classes.

2. Don't access invalid pointers

While the above is the most important part of the answer (because it will change your program entirely), there are also other things that need to be underlined. The command:

char* y=(char*)x;

Creates a char pointer that points to the address of x. It DOES NOT copy the object. Thus, when you later do:

delete x;

The pointer to y becomes invalid. When you later try to write that to a file:

std::ofstream out("test11.txt",std::ofstream::out | std::ofstream::trunc);
out.write(y,sizeof(One1));

You access memory that doesn't belong to you. At least not anymore, since you specificaly said you don't need it, by calling delete earlier.

Community
  • 1
  • 1
Paweł Stawarz
  • 3,952
  • 2
  • 17
  • 26
  • Regarding the vtable, isn't that a general problem here with *any* kind pointer? – Christian Hackl Mar 16 '15 at 19:20
  • No, POD structures have no vtable. It's just when you add polymorphism (specifically virtual member functions) that you add a vtable to the structure. – Blindy Mar 16 '15 at 19:23
  • @ChristianHackl You mean _"pointer to any class"_ or _"any pointer inside a class"_? If the first: __no__, if the second: __mostly yes__ (although it depends on where it actually points, pointing to `0` is fine) – Paweł Stawarz Mar 16 '15 at 19:24
  • @PawełStawarz: The latter. – Christian Hackl Mar 16 '15 at 20:21
  • @ChristianHackl well the OP code has no pointers in the class itself. Also - since I've mentionned POD types - I guess including a pointer makes the constructor non-trivial, which makes the class unable to be a POD. – Paweł Stawarz Mar 16 '15 at 20:25
  • @PawełStawarz do you know if this is the case in .Net as well? – Social Developer Jan 15 '20 at 12:08
  • You have to be more specific @Sumith . WHAT is the case in .Net? There are a lot of statements in this post, some of them are probably true in .Net. – Paweł Stawarz Jan 15 '20 at 18:18
  • @PawełStawarz I was referring to your answer about the potentially hazardous serialization - persistance - deserialization of a polymorphic type, due to addressess in vtable not guaranteed to point to valid functions anymore. I was wondering if this is the case in .Net as well (binary serialization). But since my last post I have learned that .net persists type information and mandates that the assembly containing the deserialized type be loaded before the event. Reading it together, I am assuming that .Net automatically corrects the vtable addressess on deserialization, correct? – Social Developer Jan 17 '20 at 09:18
  • I must admit I haven't dug this deep into .Net to be sure of the answer, but the information at https://stackoverflow.com/questions/2413483/virtual-method-tables suggests that it's the same in .net and C++. – Paweł Stawarz Jan 17 '20 at 17:24