File handling in c++ copying data to object of class - c++

#include<iostream>
#include<string>
#include<fstream>
using namespace std;
class telephone
{
string name;
long number;
public :
void getdata();
void display();
};
void telephone :: getdata()
{
cout<<"Enter the name : ";
getline(cin,name);
cout<<"Enter the number : ";
cin>>number;
}
void telephone :: display()
{
cout<<"1. Name : "<<name<<endl;
cout<<"2. Number : "<<number<<endl;
}
int main()
{
fstream f;
telephone p,q;
f.open("dir.txt",ios::out);
p.getdata();
f.write((char*)&p,sizeof(telephone));
f.close();
f.open("dir.txt",ios::in);
while( f.read((char*)&q,sizeof(telephone)))
{
q.display();
}
f.close();
return 0;
}
I have written this code to write and read data from file in class object.It displays the output but shows some error.
OUTPUT :
Enter the name : rahul
Enter the number : 234546
1. Name : rahul
2. Number : 234546
*** Error in `./a.out': double free or corruption (fasttop): 0x08f861a8 ***
Aborted (core dumped)
I have tried by using file extension like .txt,.bin,.dat but it showed the same error.Please help me to remove this error.

Writing a telephone to a file as a binary blob will not work. telephone contains name and name is a std::string. A std::string does not typically contain the string data it represents; it contains a pointer to the string data.
So when you
f.write((char*)&p,sizeof(telephone));
what you actually wrote to the file was not the string data, but a pointer to the string data. This means that
f.read((char*)&q,sizeof(telephone));
reads back p's pointer to q and that means that p and q both point at the same string data. This is bad.
When p or q go out of scope and are destroyed, they destroy name, and name, like a good little std::string, and frees the memory it points at. This leaves the other object containing a std::string pointing at memory that has been freed, and sooner or later that other object will either be used and invoke undefined behaviour or be destroyed and attempt to free the previously freed memory. This is what is meant in the error message by "double free". The same memory has been freed twice.
In your case, if q is deleted before p, q releases the memory that both p and q point at leaving p pointing at an invalid memory location. A few nanoseconds later p is deleted and p cannot free the already freed memory.
To get around this you must ensure the contents of the std::string are written to the file and then read back. This is called serialization.
Typical solutions to this problem are to write the data to the file in a text format with << and read it back with >> (which may require you to implement the << and >> operators for your class)
class telephone
{
string name;
long number;
public :
void getdata();
void display();
friend std::istream & operator<<(std::istream & out, const telephone & tp);
friend std::ostream & operator>>(std::ostream & in, telephone & tp);
};
or add serialization functions to the class
class telephone
{
string name;
long number;
public :
void getdata();
void display();
bool serialize(std::iostream & out);
bool deserialize(std::iostream & in);
};
The writing of these functions is probably the point of this assignment, so I'll stop here. Don't worry. Both are approaches are exceedingly well documented online if you have trouble. I recommend starting with the first option. It is much easier to debug because you can read the text file to see if you got it wrong.

Related

Exception thrown while reading elements from a binary file (Exception thrown: read access violation. _Pnext was 0xB414D4.)

first of all i made a simple class:
class test
{
public:
test(string name="",int age=0);
void getData();
void show();
private:
string name;
int age;
};
test::test(string name,int age)
{
this->age = age;
this->name = name;
}
void test::getData()
{
cin >> age;
cin >> name;
}
void test::show()
{
cout << "AGE:" << age<<"\n";
cout << "NAME:" << name << "\n";
}
in my main function called the getData() method to input values from user and then saved them in a binary file.
Now when i try to read the file, it does store the value in the new object of the class but i get an exception (Exception thrown: read access violation. _Pnext was 0xB414D4.)
my main function looks like this :
int main()
{
ifstream os("new.dat", ios::binary);
test b;
os.read(reinterpret_cast<char*>(&b), sizeof(b));
b.show();
return 0;
}
The issue here is that you are trying to read the test object as if it is a simple flat object. For many, many reasons, this is almost always a bad idea.
In your case, the std::string member is not a simple object at all! Under the hood, std:: string usually has at least 1 pointer member to where it has allocated the string.
If you simply save the stest object in one session, and restore the binary representation in another session, then you set these pointers to addresses that are now completely garbage.
The process of saving a data structure in a way that is later recoverable is called serialisation, and is a complex subject.
The issue here is that you are trying to read the test object as if it is a simple flat object. For many, many reasons, this is almost always a bad idea.
In your case, the std::string member is not a simple object at all! Under the hood, std:: string usually has at least 1 pointer member to where it has allocated the string.
If you simply save the test object in one session, and restore the binary representation in another session, then you set these pointers to addresses that are now completely garbage.

Why is this program crashing ? Did I wrongly allocate memory?

Question
Design a class Employee with name and employee number. Derive Manager, Scientist and Laborer classes. The manager class has extra attributes title and dues. The scientist class has extra attribute number of publications. The Laborer class has nothing extra. The classes have necessary functions for set and display the information.
My solution
#include<iostream>
#include<cstring>
using namespace std;
class employee
{
protected:
char *name;
int number;
public:
employee()
{
cout<<"enter employee name \n";
cin>>name;
cout<<"enter employee number \n";
cin>>number;
}
void display()
{
cout<<"name \t"<<name<<endl;
cout<<"number \t"<<number<<endl;
// inside class function is a inline function
}
};
class manager: private employee
{
float due;
char *title;
public:
manager( )
{
cout<<"due\t "<<endl;
cin>>due;
cout<<"title\t"<<endl;
cin>>title;
fflush(stdin);
}
void display()
{
employee::display(); //inside class function is a inline function
cout<<"due\t"<<due<<endl;
cout<<"title\t"<<title<<endl;
//inside class function is a inline function
}
};
class labour :private employee
{
public:
void display()
{
employee::display(); //inside class function is a inline function
}
};
class Scientist :private employee
{
int number;
public:
Scientist()
{
cout<<"publication number "<<endl;
cin>>Scientist::number;
}
void display()
{
employee::display();
cout<<" pub number "<<Scientist::number<<endl;
fflush(stdin);
} //inside class function is a inline function
};
int main()
{
manager m;
m.display();
Scientist s;
s. display();
labour l;
l.display();
return 0;
}
You don't allocate any memory for title or name, so you can't read into them from std::cin. Instead of using char* you should use std::string which will do all of the allocation for you:
std::string title;
std::string name;
In the constructor of empolyee you read into an uninitialized char*. Therefore it does not point to a valid block of memory where you could store the entered name to. You could do
name = static_cast<char*>(malloc( 32 * sizeof(char) ));
to allocate memory such that name points to valid memory, but you always waste memory or do not have enough for the input. Also you have to free the memory in the destructor.
As Peter Schneider wrote in the comment of this answer, another option is to use arrays of a fixed size as a member, e.g.
char name[MAX_NAME_LENGTH];
with a e.g. preprocessor defined
#define MAX_NAME_LENGTH 64
at the top of your file. This way the copy constructor does his job. With pointers as members, you always have to write them yourself, otherwise, the original class instance and copied instance will have a member pointer pointing to the same memory. So if one the copied instance changes the name, the original instance will have a changed name, too.
The easiest solution would be to use a std::string instead of char*. It allocates memory on its own and you don't have to free anything and copying works fine, too.

Simple File processing in C++

Trying to read a record of students with their name and numbers.
Writing to the file seems fine. However, reading from it prints a never-ending output. The statement - while(!file.eof()) - is causing the problem. But it's how I read the remaining person_details. Your help would be greatly appreciated.
#include <iostream>
#include <fstream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::fstream;
using std::string;
using std::ios;
class telephone
{
protected:
string name;
int number;
public:
void getDetails();
void printDetails() const;
};
void telephone:: getDetails()
{
cout<<"Enter name : "; getline(cin,name);
cout<<"Enter number : ";cin>>number;
}
void telephone:: printDetails() const
{
cout<<"Name : "<<name<<endl;
cout<<"Number : "<<number<<endl;
}
int main(int argc, char const *argv[])
{
telephone person;
fstream file("telefile.txt",ios::in | ios::out | ios::binary | ios::app);
if (!file)
{
cout<<"Invalid file name."<<endl;
return 1;
}
//writing
char choice;
do{
cout<<"----------"<<endl;
cout<<"Person : "<<endl;
cout<<"----------"<<endl;
person.getDetails();
file.write(reinterpret_cast<char*>(&person),sizeof(person));
cout<<"Enter one more?";
cin>>choice;cin.ignore();
}while(choice == 'y');
//reading
file.seekg(0);
file.read(reinterpret_cast<char*>(&person),sizeof(person));
while(!file.eof())
{
cout<<"----------"<<endl;
cout<<"Person : "<<endl;
cout<<"----------"<<endl;
person.printDetails();
file.read(reinterpret_cast<char*>(&person),sizeof(person));
}
return 0;
}
Your basic problem is that you're making a binary image of a std::string object, but a string object doesn't contain the character data, only a pointer to it (this is what allows a string to vary in size). So when you read that pointer back in, you'll start accessing whatever is in that memory location now, not what that memory held when you wrote the file. oops. Even worse things will happen when the string destructor runs and tries to free that pointer.
What you should do instead is write person.name.size() to the file, followed by that many bytes starting at &person.name[0]. Then when you read in the size, you can person.name.resize(size_from_file) and then read that many bytes into &person.name[0].
The Standard actually has a formal name for data types you are allowed to take binary images of: trivially copyable. The requirements are set out in section 9 and your telephone type doesn't meet them:
A trivially copyable class is a class that:
has no non-trivial copy constructors,
has no non-trivial move constructors,
has no non-trivial copy assignment operators,
has no non-trivial move assignment operators, and
has a trivial destructor.
The compiler-generated special member functions for telephone are non-trivial because of the std::string member, so instead of meeting all five requirements, you don't meet any of them.
Everyone has mentioned while (!file.eof()). You're not actually doing it wrong, but there is room for improvement.
First, you can catch more errors using while (file.good()) instead. Right now if you have any failure other than EOF, your loop never terminates, which matches your symptom.
Next, streams have a conversion to bool which is equivalent to calling good(), so you can write while (file). Finally, read like most other stream I/O functions returns the original stream, so you can write
while (file.read(buffer, size))
and avoid duplicating the read call both above and inside the loop.
But you didn't fall into the very common trap of checking eof() before doing the read that actually ran into the end. Bravo for that.

File handling in C++ gives .exe stopped working error

I have basic file handling code of reading file named"student.dat" using visual studio.
the output reads the file and displays the result in console but visual studio pops up the dialog as
code:
#include<iostream>
#include<fstream>
#include<conio.h>
#include<string>
using namespace std;
class student
{
int admno;
// char name[20];
string name;
public:
void getdata()
{
cout<<"\n\nEnter The Name of The Student ";
//gets(name);
cin>>name;
getch();
cout<<"\nEnter The admission no. ";
cin>>admno;
// getch();
}
void showdata()
{
cout<<"\nAdmission no. : "<<admno;
cout<<"\nStudent Name : ";
cout<<name;
//puts(name);
}
int retadmno()
{
return admno;
}
};
int main()
{
student obj;
ifstream fp1;
fp1.open("student.dat",ios::binary);
while(fp1.read((char*)&obj,sizeof(obj)))
{
obj.showdata();
}
fp1.close();
return 0;
}
You are allowed to load raw data only to some POD objects. This line is an error:
fp1.read( (char*)&obj,sizeof(obj) );
because student contains std::string. std::string contains a pointer to a memory block which becomes invalid and totally useless after the object is destroyed. It means the data you load to std::string is just a garbage.
I'd think about object serialization. Boost serialization is a good way to start.
You try to store/load an object to a file, which is invalid.
while(fp1.read((char*)&obj,sizeof(obj)))
Objects may contain references to memory (std::string e.g.) that are invalid after loading. You have to provide serialization for your objects or use plain structs without pointers or references to store your data. E.g.:
struct {
int admno;
char[20] name;
}
If you need strings with variable length, you may use a string store:
struct {
int admno;
size_t name_index;
}
std::vector<std::string> names;
To store, iterate over your names and save length and .c_str of each std::string. To read, do it the other way around and read length into n, read n bytes into a buffer and construct a std::string out of the buffer.
You might have a look at http://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/index.html
You never call GetData so admno is not instantiated in ShowData

Why I cannot receive a characters in C++

This is my code to receive string.
But I don't know what causes a problem.
Anybody knows?
#include<iostream>
#include<cstring>
using namespace std;
class person{
private:
char name[100];
//char * name;
public:
person(void) : name(){}
person(person& myPerson);
~person(void) {delete []name;}
void read();
void write();
};
person::person(person& myPerson ){
strcpy(name ,myPerson.name);
}
void person::read(){
cout<< "read name from console: " ;
cin >> name;
}
//////// <<---- problem point.
void person::write(){
cout << name << endl;
}
void main(void) {
person p1;
p1.read();
//p1.write();
getchar();
}
//I insert "getchar()" to remain console window for a while
Remove delete []name; from the destructor of person. You should only free memory that is heap allocated, with malloc() or new, and not space that is stack allocated, like your fixed-size array declared char name[100]. This change will fix the memory error you receive when the destructor is executed as the program exits.
Your code has a number of errors or bad style.
First, it's recommend to use C++ standard library facilities as much as possible. So use std::string instead of char[SIZE].
Second, use int main{return 0;} instead of void main{}.
Third, delete [] name; will lead to a memory error. You are freeing stack memory not heap memory.
Last, in my opinion, class names should start with a capital letter (Person).