我写了一个简单的程序来学习如何使用随机访问填充。它可以很好地编译,但在运行时会出现访问冲突错误。我只是在写和读一条记录。
标题文件:
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
#ifndef HEADER_H
#define HEADER_H
class info
{
private:
int id;
string name;
public:
info(int = 0, string = " ");
void set(int, string);
void display();
void write();
void read();
};
#endif
实施文件:
#include<iostream>
#include<string>
#include<fstream>
#include"Header.h"
using namespace std;
info::info(int x, string y)
{
set(x, y);
}
void info::set(int x, string y)
{
id = x;
name = y;
}
void info::display()
{
cout << "\n\n\tid : " << id;
cout << "\n\tname" << name;
}
void info::write()
{
ofstream o("info.dat", ios::out | ios::binary | ios::app);
info a(id, name);
info *p = &a;
o.write(reinterpret_cast<const char *>(p), sizeof(info));
o.close();
cout << "\n\n\tWrite Successful";
}
void info::read()
{
ifstream i("info.dat", ios::in | ios::binary);
i.seekg(0);
info a(0, "a");
info *p = &a;
i.read(reinterpret_cast<char *>(p), sizeof(info));
i.close();
p->display();
cout << "\n\n\tRead Successful";
}
主要
#include<iostream>
#include<string>
#include<fstream>
#include"Header.h"
using namespace std;
void main()
{
info a(10, "muaaz");
a.write();
a.display();
info b(2, "m");
b.read();
}
读取功能后发生错误。在read函数结束时cout“Read Successful”运行正常,之后在main中没有其他语句。我不知道造成错误的原因。
答案 0 :(得分:1)
代码中reinterpret_cast
的每次出现都会给你一个强烈的暗示,即发生危险和容易出错的事情。
void info::write() { ofstream o("info.dat", ios::out | ios::binary | ios::app); info a(id, name); info *p = &a; o.write(reinterpret_cast<const char *>(p), sizeof(info)); o.close(); cout << "\n\n\tWrite Successful"; }
这无法正常工作。除了所有语言 - 律师问题,请考虑这里的实施观点。 std::string
通常不直接包含(作为数据成员)它所代表的文本,而是包含指针以动态分配存储文本的内存。
所以你最终要做的就是在文件中写一个内存地址。
对于任何序列化业务来说,这已经足够了,因为很明显,对于同一个程序的不同运行,内存地址是不同的。
但是,当在同一个执行中写入和读取文件时,即使在这个简单的示例中它也不会起作用。 a
是一个本地对象。当write
完成时,对象将被销毁,并包含它所包含的std::string
。当std::string
被销毁时,它已为其内容分配的内存被释放。
因此,只要write
完成,您的文件就会包含无效的内存地址。片刻之后,在read
函数中,您尝试将该无效地址填充到新的std::string
中。幸运的是,您会收到访问冲突,以便您发现错误。如果你运气不好,程序会在一段时间内继续产生所需的行为,只会在以后开始崩溃或做一些奇怪的事情。
使情况变得更加复杂的一些问题:
C ++标准未指定std::string
的内存布局。因此,具有不同编译器设置的不同编译器将生成不同的info.dat
文件,即使您从完全相同的C ++源代码编译它们也是如此。
std::string
可能会也可能不会使用小字符串优化(SSO),这种技术意味着对于只有少数字符的字符串,文本 直接存储在对象而不是动态分配。所以你甚至不能假设存在指针。
另一方面,std::string
甚至可以包含更多指向动态分配内存的指针,指向所表示文本的末尾和分配内存的末尾(对于capacity
成员函数)。
考虑到所有这些重要的内部复杂性,您试图绕过或忽略所有这些内容的简单reinterpret_cast
会调用未定义的行为,这不应该让您感到惊讶。
在C ++ [*] 中,非POD类型的序列化和特别是反序列化并不容易,并且尚未标准化。您可以考虑使用第三方库。与往常一样,Boost在第三方库方面值得一试,事实证明,它确实包含一个序列化库,即Boost.Serialization。
[*] 实际上,在任何语言中都不容易,甚至在那些假装容易使用的语言中也是如此,例如Java具有高度危险的Serializable
接口。功能