你能写一个多态的类到磁盘并生存吗?

时间:2010-07-30 14:44:21

标签: c++ polymorphism

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

我的问题是:我可以将多态类写入磁盘,然后在以后读取它而不是未定义的行为吗?我猜不会因为vtables(我认为这些是在运行时生成的并且对象是唯一的?)

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;

感谢 - 你!

3 个答案:

答案 0 :(得分:1)

我建议你看看Boost Serialization

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

答案 1 :(得分:0)

如果在编写它们的程序的相同执行期间总是回读这些对象,那么可能能够侥幸逃脱它(尽管我真的不推荐它)。但是如果文件中的数据必须在程序的不同执行之间保持不变,那么使用内存中对象的原始字节几乎肯定会导致严重的问题。

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

因此,如果一个对象有一个vtable指针,并且你将该对象的原始字节写入磁盘,那么vtable指针也会被写入磁盘。如果你在同一个程序执行期间读回这些字节并将它们推送到一个合适的对象,它可能工作,因为vtable仍然在同一个位置,因此vtable指针仍然是正确的。

(但请注意,我刚才解释的所有内容都是实现细节。虽然许多编译器通常以这种方式实现虚函数,但我认为C ++标准并不保证任何确切的细节。所以可能是其他潜在的问题。)

现在,如果这可能,为什么不将这些对象存储更长的时间?因为您无法保证任何特定虚拟表将位于同一内存位置。

对于同一程序的每次执行,某些操作系统可能change the memory layout。我不知道这是否会影响虚拟桌面位置,但这肯定是一个严重的风险。

此外,如果您编译了该程序的新版本,则每个虚拟表的位置完全取决于编译器的奇思妙想。对代码中看似无关的部分的更改可能会导致编译器将相关的虚拟表放在不同的位置。显然,这种情况将彻底打破这一计划。你无法防止它发生。

(除了vtable之外,如果需要在程序的后续版本中将新数据成员添加到这些对象中,您可能必须处理将原始对象的字节的过去版本读入具有新成员的新版本或成员的不同布局。这可能会变得复杂,丑陋以及容易出错。)


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

此外,这些对象以及将其直接写入磁盘的方案需要附有评论和文档警告我所描述的所有危险。否则,一些未来的程序员可能会在不知不觉中决定添加错误类型的数据成员。或者甚至更糟糕的是,他们可能会决定尝试存储这些对象的时间长于程序的执行时间,从而导致严重的崩溃和失败,直到将来可能不会发生(可能在最糟糕的时间)。


最后,我强烈建议使用以专门用于文件的格式存储数据的方案。正如其他人已经提到的那样,Boost Serialization是一个不错的选择。如果不是这样,可能还有其他可用的序列化库。或者根据您的需要,您可以毫不费力地推出自己的机制。

答案 2 :(得分:-1)

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

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