-3

I'm trying to read from a Person.txt file that I have already wrote a Person object to. I get this error: Exception thrown: read access violation. _Pnext was 0x87E504.

I've tried debugging and it executes the function just fine. It only pops the error when the function has been complete.

Here is my code for reading from the file:

void readFromFile()
{
    ifstream inFile("Person.txt", ios::in);

    Person p2;

    inFile.read(reinterpret_cast<char*>(&p2), sizeof(Person));

    cout << "First Name: " << p2.getFirst() << endl
        << "Last Name: " << p2.getlast() << endl
        << "Gender: " << p2.getgender() << endl
        << "Age: " << p2.getAge() << endl;



}

Person class:

Class Person {
public:
    Person()
    {
        firstName = "N/A";
        lastName = "N/A";
        gender = "N/A";
        age = 0;
    }

    Person(std::string first, std::string last, std::string gender, int age)
    {
        firstName = first;
        lastName = last;
        this->gender = gender;
        this->age = age;
    }

    ~Person()
    {
        std::cout << "Person Destructor" << std::endl;
    }

    void setFirst(std::string first)
    {
        firstName = first;
    }

    std::string getFirst() const
    {
        return firstName;
    }

    void setLast(std::string last)
    {
        lastName = last;
    }

    std::string getlast() const
    {
        return lastName;
    }

    void setGender(std::string gender)
    {
        this->gender = gender;
    }

    std::string getgender() const
    {
        return gender;
    }

    void setAge(int age)
    {
        this-> age = age;
    }

    int getAge() const
    {
        return age;
    }

private:
    std::string firstName;
    std::string lastName;
    std::string gender;
    int age;
};

  • 1
    What does the declaration of `Person` actually look like? I suspect it contains pointers to dynamically allocated data, such as inside of `std::string` data members. If so, then you will not be able to write/read `Person` objects as-is the way you are, you will need to *serialize* the members individually instead. – Remy Lebeau Jun 19 '19 at 01:46
  • I edited it to show Person class ! – Adam Winters Jun 19 '19 at 01:51
  • 2
    Any time you do `reinterpret_cast` you know your code is wrong. In this case, completely wrong. –  Jun 19 '19 at 01:55
  • Damn i guess my $200 textbook is trash ahah. How do I fix this? – Adam Winters Jun 19 '19 at 02:00
  • As I suspected, your class has `std:::string` members, so you can't use things like `inFile.read(reinterpret_cast(&p2), sizeof(Person));` and `outFile.write(reinterpret_cast(&p2), sizeof(Person));` with your `Person` objects. `std::string` contains a pointer to data stored elsewhere in memory, which you are not taking into account. – Remy Lebeau Jun 19 '19 at 02:01
  • Ohh okay that makes sense ! Its weird though because I'm following my textbook and that's exactly what they do. – Adam Winters Jun 19 '19 at 02:03
  • What textbook is this? A hint: any textbook that costs $200 is probably complete trash. –  Jun 19 '19 at 02:04
  • Then either your textbook is wrong, or you are interpreting its teachings the wrong way. – Remy Lebeau Jun 19 '19 at 02:05
  • C++ How to Program (Tenth Edition) Author: Paul J. Deitel and Harvey Deitel – Adam Winters Jun 19 '19 at 02:06
  • See [The Definitive C++ Book Guide and List](https://stackoverflow.com/questions/388242/) – Remy Lebeau Jun 19 '19 at 02:09
  • I figured it out, I misread the class creation, they initialize what I have as strings, as chars. – Adam Winters Jun 19 '19 at 02:09
  • Not a great book. And certainly not at that price! –  Jun 19 '19 at 02:09
  • 1
    @AdamWinters *Its weird though because I'm following my textbook and that's exactly what they do.* -- So finally the mystery that has been on my mind for years may be solved. You are not the only one who has written code this way, and I've always wondered who or what is teaching this method. There are thousands of questions tagged C++ with the very same issue, and this wrong technique couldn't have just be picked up randomly by the unwary learner of the language. – PaulMcKenzie Jun 19 '19 at 02:10
  • If you replace the `std::string` members with `char[]` members, then your `ofstream::write()`/`ifstream::read()` approach will work just fine, since no pointers would be involved anymore. – Remy Lebeau Jun 19 '19 at 02:20
  • @AdamWinters -- There is no way that `inFile.read(reinterpret_cast(&p2), sizeof(Person))` could ever work if you think about it logically. The `sizeof(Person)` remains the same value, regardless of how much data the `string` members hold. It could be a million characters in the string, the `sizeof(Person)` is a mere, I don't know, maybe 120 or so bytes. So how would you expect to save just a 120 bytes or so of data with `std::string` data members that could hold millions of characters and expect this to work? Super-duper data compression under the hood? – PaulMcKenzie Jun 19 '19 at 02:21
  • @PaulMcKenzie "*There is no way that `inFile.read(reinterpret_cast(&p2), sizeof(Person))` could ever work if you think about it logically*" - when using `std::string` members, that is indeed true. When using `char[]` members, that is not true at all. – Remy Lebeau Jun 19 '19 at 02:25
  • 2
    @RemyLebeau Is that really true though? Are you guaranteed that the padding between `gender` and `age` won't change with compilation flags, compiler version, or other things? Are you guaranteed that the byte representation for `age` won't change with CPU versions, OS versions, or other things? It *might* work, but it also might not and teaching people to code that way in a book is reckless. – David Schwartz Jun 19 '19 at 02:49

1 Answers1

1

Your Person class has std::string members, which contain pointers to dynamically allocated data stored elsewhere in memory. As such, you will not be able to write/read Person objects using ofstream::write() and ifstream::read() the way you are trying to do. You will only be writing/reading the pointer values, not the actual character data being pointed to. You must serialize the data members individually instead. For instance, in this example, you can simply write each string to its own line in the text file, eg:

void writeToFile()
{
    ofstream outFile("Person.txt");

    Person p2("Joe", "Smoe", "M", 25);

    //outFile.write(reinterpret_cast<char*>(&p2), sizeof(Person));
    outFile << p2.getFirst() << "\n"
        << p2.getlast() << "\n"
        << p2.getgender() << "\n"
        << p2.getAge() << "\n";
}

void readFromFile()
{
    ifstream inFile("Person.txt");

    Person p2;
    string s;
    int i;

    //inFile.read(reinterpret_cast<char*>(&p2), sizeof(Person));
    getline(inFile, s);
    p2.setFirst(s);
    getline(inFile, s);
    p2.setLast(s);
    getline(inFile, s);
    p2.setGender(s);
    inFile >> i;
    inFile.ignore(numeric_limits<streamsize>::max(), '\n');
    p2.setAge(i);

    cout << "First Name: " << p2.getFirst() << endl
        << "Last Name: " << p2.getlast() << endl
        << "Gender: " << p2.getgender() << endl
        << "Age: " << p2.getAge() << endl;
}

I would suggest taking it a step further and move the writing/reading logic into the Person class itself:

Class Person {
public:
    Person()
    {
        firstName = "N/A";
        lastName = "N/A";
        gender = "N/A";
        age = 0;
    }

    Person(std::string first, std::string last, std::string gender, int age)
    {
        firstName = first;
        lastName = last;
        this->gender = gender;
        this->age = age;
    }

    ~Person()
    {
        std::cout << "Person Destructor" << std::endl;
    }

    void setFirst(std::string first)
    {
        firstName = first;
    }

    std::string getFirst() const
    {
        return firstName;
    }

    void setLast(std::string last)
    {
        lastName = last;
    }

    std::string getlast() const
    {
        return lastName;
    }

    void setGender(std::string gender)
    {
        this->gender = gender;
    }

    std::string getgender() const
    {
        return gender;
    }

    void setAge(int age)
    {
        this-> age = age;
    }

    int getAge() const
    {
        return age;
    }

    ostream& write(ostream &out) const
    {
        out << firstName << "\n"
            << lastName << "\n"
            << gender << "\n"
            << age << "\n";
        return out;
    }

    istream& read(istream &in)
    {
        getline(in, firstName);
        getline(in, lastName);
        getline(in, gender);
        in >> age;
        in.ignore(numeric_limits<streamsize>::max(), '\n');
        return in;
    }

private:
    std::string firstName;
    std::string lastName;
    std::string gender;
    int age;
};

ostream& operator<<(ostream &out, const Person &p)
{
    return p.write(out);
}

istream& operator>>(istream &in, Person &p)
{
    return p.read(in);
}

Then you can do this:

void writeToFile()
{
    ofstream outFile("Person.txt");

    Person p2("Joe", "Smoe", "M", 25);

    outFile << p2;
}

void readFromFile()
{
    ifstream inFile("Person.txt");

    Person p2;

    inFile >> p2;

    cout << "First Name: " << p2.getFirst() << endl
        << "Last Name: " << p2.getlast() << endl
        << "Gender: " << p2.getgender() << endl
        << "Age: " << p2.getAge() << endl;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770