有没有办法从 vtable 中重建一些保存的类?
我正在将一些对象复制到文件中,它们都派生自同一类。但我希望能够在加载它们后调用它们的函数来执行该类应该执行的操作,这就是我到目前为止所做的:
#include <iostream>
#include <fstream>
using namespace std;
struct a
{
virtual void print()
{
cout << "this is a.\n";
};
};
struct b : public a
{
virtual void print()
{
cout << "this is b.\n";
}
};
int main()
{
ofstream testofile("test.bin",ios::binary);
a* tempa = new a;
a* tempb = new b;
testofile.write((char*)tempa,sizeof(a));
testofile.write((char*)tempb,sizeof(b));
testofile.flush();
testofile.close();
ifstream testifile("test.bin",ios::binary);
a* x = (a*)new char[max(sizeof(a),sizeof(b))];
testifile.read((char*)x,sizeof(a));
x->print();
testifile.read((char*)x,sizeof(b));
x->print();
}
我的示例工作正常,但如果我注释保存部分然后运行程序,则 vtable 似乎无效新运行的应用程序(尽管我的代码中没有任何变化)。问题是我的文件管理器类不知道可能从我的基础对象派生的所有可能的对象,并且我想仅使用对文件管理器的一次调用来加载它来重建我的所有应用程序结构。当然,我的每个对象都有自己的保存/加载函数,但是文件管理器应该如何猜测加载函数适合当前数据块的位置?
I'm copying some objects into a file and they all are derieved from same class. but I want to be able to call their functions after loading them to do what that class should do, here's what i did until now:
#include <iostream>
#include <fstream>
using namespace std;
struct a
{
virtual void print()
{
cout << "this is a.\n";
};
};
struct b : public a
{
virtual void print()
{
cout << "this is b.\n";
}
};
int main()
{
ofstream testofile("test.bin",ios::binary);
a* tempa = new a;
a* tempb = new b;
testofile.write((char*)tempa,sizeof(a));
testofile.write((char*)tempb,sizeof(b));
testofile.flush();
testofile.close();
ifstream testifile("test.bin",ios::binary);
a* x = (a*)new char[max(sizeof(a),sizeof(b))];
testifile.read((char*)x,sizeof(a));
x->print();
testifile.read((char*)x,sizeof(b));
x->print();
}
my example works fine but if i comment save part and then run the program it seems the vtable is invalid for the new runed application (though nothing in my code changed). the problem is my file manager class isn't aware of all possible object that may derieve from my base object and I want to rebuild all my application structure using only a single call to file manager to load it. of course each of my objects have their own save/load functions but how should the filemanager guess where is the load function appropriate for the current data chunk?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
请不要这样做。绝不。
基本上,您所做的就是使用旧式转换将
a*
转换为char*
。这会默默地导致两个不相关类型之间的reinterpret_cast
,并且高度依赖于实现。您不能依赖底层内存布局:它可能会因任何原因而改变(即使使用相同的编译器)。如果您的类包含指针,则无法保证当您重新加载类时它们指向的数据仍然存在(或者只是相同)。
如果您想提供序列化机制,请创建您自己的
serialize()
和deserialize()
函数(它们甚至可以是您自己编写的模板化函数)然后可以专门化,或者只是常规成员函数,这并不重要)。当然,这需要更多的工作,但为了可靠性。此外,这样做,您可以优化数据表示以适应任何存储类型(保存到磁盘、发送到网络等),您甚至可以更改类接口,并仍然保持与已序列化实例的兼容性。
Please, don't do that. Never.
Basically, what you do is using a old-style cast to cast a
a*
to achar*
. This results silently in areinterpret_cast
between two unrelated types, and is highly implementation dependant. You cannot rely on the underlying memory layout: it might change for any reason (even when using the same compiler).If your class contains pointers, you have no guarantee that the data they point to will still be there (or simply the same) when you reload your class.
If you want to provide a serialization mechanism, create your own
serialize()
anddeserialize()
functions (they can even be templated functions that you can then specialize, or just regular member functions, it doesn't really matter).Of course, this requires a bit more work, but for the sake of reliability. Moreover, doing so, you can optimize the data representation to fit any storage type (saved to disk, sent to network, ...) and you can even change your class interface and still keep a compatibility with the already serialized instances.
您正在编写的代码将不可移植(字节顺序)。
作为示例,基类或结构的虚拟函数表偏移量可能会改变。
在单继承的情况下,Solaris 和 Aix 编译器将 vft 放在结构/类的末尾,将 VC++ 放在 offset(0) 处。
我从来没有检查过 g++,但一些乐趣是可能的。
我不建议直接写入对象,除非您有大量数据并且不需要长期维护这些数据(即临时数据)。
vft 的值取决于动态库(dll 等)在虚拟内存中的映射位置。
如果你编写自己的分配器并修补 vft 它可能会起作用。但它非常苗条。
The code your are writing will not be portable (endianess).
As an exemple virtual function table offset of a base class or struct may change.
Solaris and Aix compiler put the vft at end of struct/class and the VC++ at the offset(0) in case of single inheritence.
I never check with g++ but some fun is possible.
I do not recommend direct write of object unless you have a bulk and you do not have to maintain these data for a long time (ie. temporary data).
The value of the vft depends where you dynamic library (dll or so) is mapped in virtual memory.
If you write you own allocator and patch the vft it may work. But it is quite slimmy.
您需要一种序列化机制,通过该机制您的派生序列化方法在基础上调用序列化,然后首先序列化一些 id 以指示派生类型 (typeid),然后序列化它自己的成员。
要从序列化文件中重构对象,您需要一个工厂对象来读取文件并确定类型(根据派生的序列化 typeid),实例化该类型的对象,并调用对象上的 deserailize 方法以从文件中加载它,通过基类指针返回对象。
You want a serialization mechanism whereby your derived serialize method call serialize on the base and then serializes first some id to indicate the derived type (typeid) then its own members.
To reconstitute the objects from the serialized file you want a factory object that reads the file and determines type (from the derived serialize typeid), instatiates an object of that type, and calles the deserailize method on the object to load it from the file and return the object via a base class pointer.