我有以下类,我以二进制模式保存在文件中:
class lol
{
public:
~lol() {
std::cout << "destucted" << std::endl;
system("pause");
}
int age;
std::string name;
};
现在我的主要内容是:
int main()
{
{
lol kar;
kar.age = 17;
kar.name = "Blabla";
ytxt("class").Write(std::string((char*)&kar, sizeof(kar))); //Saves the class in binary mode
}
lol * asd;
{
std::string stClass = ytxt("class").Read();//reads the file in binary
asd = (lol*)new char[stClass.length()];//asd is now allocated with new
memcpy_s(asd, stClass.length(), stClass.c_str(), stClass.length());//copy the content of the file into the class
}
std::cout << "Name: " << asd->name << std::endl;//output
std::cout << "Age: " << asd->age << std::endl;
asd->age = 79;
std::cout << "Age: " << asd->age << std::endl;
delete asd;//I cannot delete it as lol
//delete (char*)asd;//works but lol destructor is not called
system("pause");
}
正在向控制台打印asd
指针的内容,删除指针并调用析构函数。最后会抛出异常。
当我不删除asd
指针时,一切正常,但asd
指针的析构函数未被调用。我知道指针被分配为new char
,我必须将其删除为new char
,但还有另一种方法是删除操作调用lol
的析构函数吗?
答案 0 :(得分:4)
ytxt("class").Write(std::string((char*)&kar, sizeof(kar)));
这一行确实保存了一个字节转储。但是你不能加载那个字节转储,并期望它再次成为你的类的正确实例。这不是二进制序列化的工作原理。这仅适用于POD类型(也可能不适用于那个确切的行)。如果您想保存数据并在以后加载,则需要使用其他方法。
程序中的每个其他问题都归结为具有正确数据的字节垃圾。
答案 1 :(得分:3)
即使您修复了析构函数,此代码也无法正常工作:存储包含指针的对象的字节,然后尝试将该对象恢复到另一个内存中是不正确的。
您的lol
类有std::string
,它存储指向字符串实际字节的指针。将kar
的字节复制到文件中时,会存储正在运行的程序中的指针,而不会存储字符串本身的内容。
从文件中恢复lol
的实例时,其指针指向已由kar
的析构函数解除分配的内存块。这是未定义的行为。
您需要更改序列化对象的方法。您目前拥有的方法无法挽救。
注意:将类放入分配为字节数组的内存中的情况需要 placement new
运算符。有关如何在放置新内容后进行清理的信息,请参阅this Q&A。
答案 2 :(得分:2)
由于你的lol类包含一个std :: string,它是一个复杂的对象并且有内部内存管理,当它实际上不是真正的对象而是文件中的一些随机字节时,试图删除它会导致内存损坏/崩溃。
答案 3 :(得分:1)
只有一些C ++结构可以作为原始字节写入和读取,并以合理的方式工作。以这种方式工作的那种称为&#34;普通旧数据&#34;或简称pod。 (在C ++ 11中,他们将其分解为更细粒度的概念,但这是一个细节。)
结构或类(除了默认的public vs private之外),除了pod,没有虚拟,没有析构函数,没有构造函数,内置类型(如int
或者指针和数组)之外什么都不包含旧数据。
将这些作为比特流读取/写入至少做了一些明智的事情。
如果它包含指针,指向的东西的地址将在程序重启后继续存在,所以请避免使用。
std::string
不符合条件,也不包含任何内容。
要从文件中保存/加载,您应该查看序列化问题。有许多技术,但没有一种是免费的。
很容易为某些输出存档类型save( put_here, X const& _
定义一个免费的put_here
函数。然后为您要支持的每种类型创建重载。对load
执行相同操作。对于复合类型,只需保存/加载每个元素。
为版本控制添加标签等在非平凡的代码中也很有用。
对于像字符串这样的序列容器,您可以将其保存为save(here, str.size()); for (auto&&c:str)save(here, c);