gpt4 book ai didi

c++ - 从对象C++中的文件读取内容时发生段错误

转载 作者:搜寻专家 更新时间:2023-10-31 00:26:23 24 4
gpt4 key购买 nike

首先,在我的代码中,我将名称和手机号码存储在一个对象中,然后使用fstream.write()方法将该对象写入一个文本文件中。它可以成功工作,但是当我将写入的内容读入另一个对象并调用display方法时,它将正确显示数据,但是在打印数据后却给我带来了分割错误。
这是我的代码-

#include<iostream>
#include<fstream>
using namespace std;
class Telephone
{
private:
string name="a";
int phno=123;
public:
void getTelephoneData()
{
cout<<"Enter Name:";
cin>>name;
cout<<"Enter Phone Number:";
cin>>phno;
}
void displayData()
{
cout<<"Name\t\tPhone no"<<endl;
cout<<name<<"\t\t"<<phno<<endl;
}

void getData() {
Telephone temp;
ifstream ifs("Sample.txt",ios::in|ios::binary);
ifs.read((char*)&temp,sizeof(temp));
temp.displayData();
}
};
int main()
{
Telephone t1;
t1.getTelephoneData();
cout<<"----Writing Data to file------"<<endl;
ofstream ofs("Sample.txt",ios::out|ios::binary);
ofs.write((char*)&t1,sizeof(t1));
ofs.close();
t1.getData();
}

请在我错的地方帮助我。
提前致谢...!

最佳答案

因此,在为您提供解决方案之前,我们先简要介绍一下这里发生的情况:
ofs.write((char*)&t1,sizeof(t1));
您正在做的是将t1转换为指向char的指针,然后说“按原样写入to1的内存表示形式”。因此,我们必须自问:t1的内存表示是什么?

  • 您正在存储一个(定义的实现,很可能是4个字节)整数
  • 您还将存储一个复杂的std::string对象。

  • 写入4个字节的整数可能没问题。它绝对不是可移植的(big-endian或little-endian),如果在具有不同字节序的平台上读取文件,则可能会得到错误的int值。

    编写 std::string绝对不行。字符串是复杂的对象,它们通常在堆上分配存储空间(尽管存在诸如小字符串优化之类的事情)。这意味着您将序列化一个指向动态分配对象的指针。这将永远无法工作,因为向后读取指针将指向您绝对无法控制的内存中的某个位置。这是未定义行为的一个很好的例子。任何事情都会发生,并且程序可能会发生任何事情,包括“看起来正确无误”,尽管存在严重的问题。
    在您的特定示例中,由于创建的Telephone对象仍在内存中,因此您获得的是指向同一动态分配内存的2个指针。当 temp对象超出范围时,它将删除该内存。

    当您返回主函数时,当 t1超出范围时,它将尝试再次删除相同的内存。

    序列化任何类型的指针都是很大的禁忌。如果对象内部由指针组成,则需要针对这些指针将如何存储在流中做出自定义解决方案,然后读取以构造一个新对象。常见的解决方案是“好像”将它们存储为按值存储一样,然后,当从存储中读取对象时,动态分配内存并将对象的内容放在同一内存中。如果尝试序列化多个对象指向内存中同一地址的情况,这显然将行不通:如果尝试应用此解决方案,最终将获得原始对象的多个拷贝。

    幸运的是,对于 std::string而言,此问题很容易解决,因为字符串已重载了 operator<<operator>>,并且您无需执行任何操作即可使其工作。

    编辑:仅使用 operator<<operator>>不适用于 std::string,稍后解释原因。

    如何运作:

    有很多可能的解决方案,我将在这里分享一个。
    基本思想是,您应该分别序列化电话结构的每个成员,并依靠每个成员都知道如何序列化自身这一事实。我将忽略跨字节序兼容性的问题,以使答案更简短,但是如果您关心跨平台兼容性,则应考虑一下。

    我的基本方法是为类电话覆盖 operator<<operator>>

    我声明了两个免费功能,它们是Telephone类的 friend 。这将使他们可以戳入不同电话对象的内部,以序列化其成员。
    class Telephone { 
    friend ostream& operator<<(ostream& os, const Telephone& telephone);
    friend istream& operator>>(istream& is, Telephone& telephone);
    // ...
    };

    编辑:我最初有用于序列化字符串的代码是错误的,所以我的评论很简单是明显的错误

    用于实现这些功能的代码令人惊讶。由于遇到空格时,字符串的 operator>>会停止从流中读取,因此使用名称不是单个单词或带有特殊字符的名称将不起作用,并且将流置于错误状态,无法读取电话号码。为了解决这个问题,我在@Michael Veksler后面跟随了这个示例,并显式存储了字符串的长度。我的实现如下所示:
    ostream& operator<<(ostream& os, const Telephone& telephone)
    {
    const size_t nameSize = telephone.name.size();
    os << nameSize;
    os.write(telephone.name.data(), nameSize);
    os << telephone.phno;
    return os;
    }

    istream& operator>>(istream& is, Telephone& telephone)
    {
    size_t nameSize = 0;
    is >> nameSize;
    telephone.name.resize(nameSize);
    is.read(&telephone.name[0], nameSize);
    is >> telephone.phno;
    return is;
    }

    请注意,您必须确保所写入的数据与稍后将要尝试读取的数据相匹配。如果存储的信息量不同,或者参数的顺序错误,则最终不会得到有效的对象。如果以后要对Telephone类进行任何形式的修改,则通过添加要保存的新字段,您将需要修改这两个功能。

    为了支持名称中带有空格的名称,还应修改从cin读取名称的方式。一种方法是使用 std::getline(std::cin, name);而不是 cin >> name
    最后,如何从这些流中进行序列化和反序列化:
    不要使用 ostream::write()istream::read()函数-改用我们已覆盖的 operator<<operator>>
    void getData() {
    Telephone temp;
    ifstream ifs("Sample.txt",ios::in|ios::binary);
    ifs >> temp;
    temp.displayData();
    }

    void storeData(const Telephone& telephone) {
    ofstream ofs("Sample.txt",ios::out|ios::binary);
    ofs << telephone;
    }

    关于c++ - 从对象C++中的文件读取内容时发生段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52685810/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com