How to write single class private data member to binary file

I have the following class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student {
private:
	string name;
	double grade;

public:
	Student();
	~Student();

	void setName(); // cin >> name
	void setName2(string name); // this->name = name
	void setGrade(); // cin >> grade
	void replaceGrade(double grade);
	string getName()const;
	double getGrade()const;
	void printOne()const;

};


Basically my program works as intended but crashes every time I exit except for the first time, which is surprising. It happens because of reading the file. If I exit the program without reading the file it won't crash. My guess is because I am using the following method to write/read to my binary file
1
2
myFile.write((char*)&st, sizeof(st));
myFile.read((char*)&st, sizeof(st));


"st" is a variable of type Student. a.k.a. an object

Since I have a string which is not a primitive data type as a private member I found out that doing it this way is wrong (I have to do serialization or something? I don't know. Serialization is something I vaguely understand and for now want to stay away from it)

So I found a post on this website about writing and reading string to/from binary file.

This is how it's done for the string:

1
2
3
4
cout << "\n Input student name: ";
st.setName(); // basically a function containing cin >> name
myFile.write(st.getName().data(), st.getName().size()); //write string to binary
myFile.write("\0", sizeof(char)); // null end string for easier reading 


Now I have to write the double to the binary file separately. However using the following method:
myFile.write((char*)&st.getGrade(), sizeof(st.getGrade()));

does not work. Neither does:

myFile.write(reinterpret_cast<char*>(&st.getGrade()), sizeof(st.getGrade()));

If I type the same code but using getName functions instead, surprisingly it works but I need it for the double. I found out it doesn't work for the double because the double is private and we're accessing it through the function getGrade() which looks like this

1
2
3
4
double Student::getGrade()const
{
   return grade;
}


If I make grade public so it is accessed directly without using methods for it it will work but I want it to be private. The compiler gives the following errors:

1. expression must be an Ivalue or a function designator
2. '&' requires I-value

It also underlines the variable st indicating where the problem is. Once again if I change the function to getName which returns the private string name it doesn't show errors and compiles sucessfully, but not with the double which is strange. My guess is that the string class has a built in function to handle such stuff?

Basically using:


1
2
myFile.write((char*)&st, sizeof(st));
myFile.read((char*)&st, sizeof(st));


does work as inteded but I am guessing that's what causing the program to crash after the first time it was executed (a.k.a. after the first time the file was created and read from it). As I said I was told that using this method to write string is incorrect as I am writing a complex data type to a file which means I am writing the pointer to the string, not the string itself, so when I read the file I load the pointer. Surpisingly the output from the file is correct, it displays the string correctly but I guess something messes up the memory every time I read from the file after the first time the program is run and when the destructor is called at program termination it crashes. This is why I decided to use a different approach with string.data() and string.size() for writing the strings to the file.

Here is the forum post that enlightened me about this method of writing string to binary: http://www.cplusplus.com/forum/beginner/166519/
Notice however that the struct in that post contains public members, this is why it will work for Texan40's code but not for mine since my double is private and it's value is returned through a method in public.

Thanks for the help in advance.
Last edited on
I found out it doesn't work for the double because the double is private and we're accessing it through the function getGrade() which looks like this

It doesn't work because you're trying to take the address of a temporary variable.

1
2
    auto grade = st.getGrade();
    myFile.write(reinterpret_cast<char*>(&grade), sizeof(grade));
It doesn't work because you're trying to take the address of a temporary variable.

1
2
auto grade = st.getGrade();
myFile.write(reinterpret_cast<char*>(&grade), sizeof(grade));



Thank you very much. This fixed my problem. I am still in process of testing my app but for now it's not crashing after the first time I read from a file. What a relief.

But could you explain what is auto? I never heard of it before. What's the magic that it's doing in this case? I have no clue when, how and why I should use it.
auto is just a way of getting a value without worrying what type it is; that is, it "auto"-matically gets the type.

In your case, you could replace it with double easily enough, and nothing would change. Still, auto is useful in these circumstances because it means that if you suddenly decided you wanted your grade to be of type int, or something entirely different, you don't have to remember to change that bit.
Thank you.

Is there any difference between

myFile.write((char*)&grade, sizeof(grade));

and

myFile.write(reinterpret_cast<char*>(&grade), sizeof(grade));

?
Again, not really. reinterpret_cast is just a safer, more explicit form of casting than the C-style cast.. It only allows transforming one type of pointer to another, e.g. double* to char*. If you forget to get a pointer, e.g. try to turn double (no star) to char*, it throws a compiler error.

On the other hand, just doing (char*)&grade is less safe, because it will happily let you turn double (no star) into char*... even though that was not what you really wanted.
Last edited on
Once again, thank you very much! Problems solved.
Topic archived. No new replies allowed.