C++ 你能在磁盘上写一个多态类并存活下来吗?

C++ 你能在磁盘上写一个多态类并存活下来吗?,c++,polymorphism,C++,Polymorphism,首先,我知道将类写入磁盘是不好的,但是您应该看到我们的一些其他代码。D: 我的问题是:我是否可以将多态类写入磁盘,然后在以后读取它,而不获得未定义的行为?我猜不是因为vtables(我认为它们是在运行时生成的,并且是对象特有的?) 即 谢谢大家! 问题不在于vtable。它是按类类型存储的,而不是按实例存储的,所以您不会将其写入文件。基本上你的代码应该可以工作(还没有试过) 但是,您应该记住,从文件中读取指针/句柄是不起作用的。我建议您看看 我们使用“序列化”这个术语来表示任意一组C++数据结构

首先,我知道将类写入磁盘是不好的,但是您应该看到我们的一些其他代码。D:

我的问题是:我是否可以将多态类写入磁盘,然后在以后读取它,而不获得未定义的行为?我猜不是因为vtables(我认为它们是在运行时生成的,并且是对象特有的?)


谢谢大家!

问题不在于vtable。它是按类类型存储的,而不是按实例存储的,所以您不会将其写入文件。基本上你的代码应该可以工作(还没有试过)


但是,您应该记住,从文件中读取指针/句柄是不起作用的。

我建议您看看

我们使用“序列化”这个术语来表示任意一组C++数据结构的可逆解构到字节序列。这样的系统可用于在另一个程序上下文中重建等效结构。根据上下文的不同,这可能用于实现对象持久性、远程参数传递或其他功能

如果这些对象总是在编写它们的程序的同一执行过程中被读回(尽管我真的不建议这样做),那么您可能可以不受惩罚。但是,如果文件中的数据必须在程序的不同执行之间保持不变,那么使用内存中对象的原始字节几乎肯定会导致严重的问题

每个vtable本身在编译时生成,并存储在生成的可执行文件中的某个位置。每个对象实例包含的只是一个指向相应vtable的指针,并且该指针在任何给定对象的生命周期内都不会更改。(多重继承可能有点复杂,但对于本次讨论而言,这些细节并不相关。指针仍然不变。)

因此,如果一个对象有一个vtable指针,并且您将该对象的原始字节写入磁盘,那么vtable指针也会写入磁盘。如果在程序的同一执行过程中读回这些字节,并将它们放入适当的对象中,则可能会起作用,因为vtable仍然位于相同的位置,因此vtable指针仍然正确

(但注意到我刚才解释的每一件事都有一个实现细节。虽然很多编译器通常以这种方式实现虚拟函数,但我认为任何精确的细节都不被C++标准所保证。因此可能会有其他潜在的问题。) 现在,如果可能的话,为什么不将这些对象存储更长的时间呢?因为您不能保证任何特定的虚拟表将位于同一内存位置

某些操作系统可能会为同一程序的每次执行而更改。我不知道这是否真的会影响虚拟表的位置,但这肯定是一个严重的风险

此外,如果编译新版本的程序,每个虚拟表的位置完全由编译器决定。对代码中看似不相关的部分所做的更改可能会导致编译器将相关的虚拟表放在不同的位置。显然,这种情况将彻底破坏这一计划。你没有办法阻止它发生

(除了vtables之外,如果在程序的后续版本中需要向这些对象添加新的数据成员,该怎么办?您可能需要处理将原始对象字节的过去版本读取到具有新成员或不同成员布局的新版本中的问题。这可能会变得复杂、难看,并且容易出错。)


现在,即使您只打算为程序的每次执行临时存储对象。我还是不认为这是个好主意。对于这些对象可以包含哪些类型的变量,您受到了很大的限制。没有智能对象(std::string、std::vector等)。没有指向每个对象分配的内存的指针。因此,任何字符串都必须存储在原始字符数组中。其他动态分配必须转换为固定成员或成员数组。这意味着无论在哪里使用这些对象,都会失去许多C++的优点

此外,这些对象和将其直接写入磁盘的方案需要附带注释和文档,以警告我所描述的所有危险。否则,一些未来的程序员可能会在不知不觉中决定添加错误类型的数据成员。甚至更糟糕的是,他们可能会决定尝试将这些对象存储在比程序执行时间更长的地方,从而使它们面临严重的崩溃和故障,而这些情况可能要到将来很久才会发生(可能是在最坏的时候)



最后,我强烈建议使用一种方案,以专门用于文件的格式存储数据。正如其他人已经提到的,Boost序列化是一个很好的选择。如果不是这样,可能还有其他可用的序列化库。或者,根据您的需要,您可以不费吹灰之力地滚动您自己的机械装置。

我可以问一下您为什么要尝试实现这一点吗?为什么不能编写序列化代码来写入和读取状态?如果你需要这类功能,你可能想用Java来代替。你通常不能通过简单的调用fwrite将任何类(多态性或非多态性)写入磁盘,并希望再次阅读。我认为你应该寻找解决问题的另一个答案,而不是问如何做像这样糟糕的事情,我的朋友:)每次有人这样做,上帝杀死一只小猫。又死了一个人,别那么做。接受Uri的建议并序列化。仅仅因为你的代码库的其他部分被污染了,并不意味着你需要编写更多被污染的代码。我从其他评论中认为这是不可能的。使用boost会很好,但我怀疑它是否会发生
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;