I've written a class that reads in a map. But I need the map to be editable outside of the class. So my question is how can I return a map.
class ReadMap
{
string fileName;
public:
//constructors and destructor
ReadMap(){fileName="blank.txt";}
ReadMap(string name){fileName=name;}
~ReadMap(){}
//Function to print out visible list
void show()
{
LineDatabase Entry;
int LineNumber=100;
string buffer;
ifstream myfile (fileName.c_str() );
while (myfile.good())
{
myfile >> LineNumber >> ws;
getline (myfile, buffer);
Entry.insert(pair<int, string>(LineNumber, buffer));
cout <<buffer << endl;
}
//return Entry;
}
};
You may be better off by having the caller of show() pass in a reference to the map to be filled, as returning a map tends to have high overhead. Something like this:
void show(LineDatabase& Entry) {
// do your map readin logic
return;
}
You return a map just like you return anything else—using the return keyword. If you're wondering why your commented out return doesn't work, it's because you declare show() as returning void. Change void show() to LineDatabase show().
Also, try to keep variable and type names lower-case. Typical convention is to use capitalized names for template parameters, so it is a bit confusing to read.
There are 4 options.
The simplest option is to change the return type of show and uncomment your return, however this will be returning the map by value which will involve a copy and could (depending upon size) be very inefficient (possibly dangerous, depending upon LineDatabase's copy operator).
LineDatabase show()
{
LineDatabase Entry;
// .... ommited
return Entry;
}
The 2nd option is to do as was suggested by user258808 and create a new object then return it by pointer, the issue with this approach is that your client would have to know to call delete on this pointer when finished otherwise you would be creating a leak.
The 3rd option is to have Entry as a field of ReadMap and then return a reference. This is my personal preference as it imposes the least burden on the client, however it may also require you to 'reset' the Entry before each new run.
Something like this
class ReadMap
{
string fileName;
LineDatabase Entry;
public:
//constructors and destructor
ReadMap(){fileName="blank.txt";}
ReadMap(string name){fileName=name;}
~ReadMap(){}
//Function to print out visible list
LineDatabase& show()
{
int LineNumber=100;
string buffer;
ifstream myfile (fileName.c_str() );
while (myfile.good())
{
myfile >> LineNumber >> ws;
getline (myfile, buffer);
Entry.insert(pair<int, string>(LineNumber, buffer));
cout <<buffer << endl;
}
return Entry;
}
};
The issue with this is that it exposes your internal state to modification, it is possible to return a const reference but then the client cannot modify the Map.
Finally, you could do as was suggested by bodes. However this requires that the client passes in a Map for you to work on.
Your choice will depend on how much work you would like to require your client to do as well as what kind of constraints you need and/or do not need to place on the data structure.
Related
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.
so basically I was trying to save a class inside a .dat file but in my code but it says this error No matching member function for call to 'open' but I put fstream header. I don't know if I'm writing something wrong. I use Xcode 10.
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
memberinformation()
{ name="not assigned" ;
phonenumber=0;
memberid=0;
}
int option3();
int option2();
int option1();
int option4();
};
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app,ios::binary) //this is where I get the error.
f.write((char*)&k,sizeof(k));
}
You are lucky to have been stopped by a simple error. #Alex44 has already shown how to get rid of the error:
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
But the following line is even worse:
f.write((char*)&k,sizeof(k));
because the compiler will not show any error, while the content of the string will not be saved in the file. std::string is not trivially copiable and because of that, the memberinformation class is not either. So you should not try to write it to a file as raw bytes.
You should instead write a serialization function that writes to a binary stream (just a possible serialization way):
phonenumber as a long int (no problem there)
memberid as an int (no problem there)
name.size as a size_t
name.data as name.size bytes
The other two answers have answered:
Why its not compiling.
Why its a bad idea to write binary objects.
I would suggest that you serialize the object via the standard C++ technique of using the stream operators. This makes writting/reading the objects trivial and usually makes debugging problems easy.
Using the format suggested by #serge-ballesta in his post:
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
// OLD STUFF GOES HERE
void swap(memberinformation& other) noexcept
{
using std::swap;
swap(name, other.name);
swap(phonenumber, other.phonenumber);
swap(memberid, other.memberid);
}
friend std::ostream& operator<<(std::ostream& str, memberinformation const& data)
{
return str << data.phonenumber << " "
<< data.memberid << " "
<< data.name.size() << " "
<< data.name << " ";
}
friend std::istream& operator<<(std::istream& str, memberinformation& data)
{
memberinformation tmp;
std::size_t nameSize
if (str >> tmp.phonenumber >> tmp.memberid >> nameSize) {
// All sizes were read correctly.
tmp.name.resize(nameSize);
if (str.ignore(1).read(&tmp.name[0], nameSize)) {
// ignored the space and read the name correctly.
// So now we update the "data" object
tmp.swap(data);
}
}
return str;
}
};
Now in your code:
int main()
{
memberinformation object;
std::cout << object;
std::cin >> object;
std::ofstream file("Data.dat");
file << object;
}
You miss a semicolon and you need to "bitwise or" your flags:
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
...
}
The answers above address your initial problem. I'm going to talk about two more.
First, you probably should f.close() at the end of your method. It may be perfectly fine to let it drop out of scope and clean up from there, but I personally think that's ugly, and I wouldn't count on it.
Second, I wouldn't store the data in binary unless there's a really good reason to do it. It won't be portable. Serge above suggests a serialization method. I'd consider an alternate approach. I'd write to the file in a human readable form such as JSON. Yes, it's a little more work, but...
-If you change your class, your old files will still be readable
-They are portable across environments
-You can actually look at them and readily understand what they contain
So Serge's suggestions above aren't horrible, but I'd pick a more modern serialization / deserialization style.
Note that your f.write won't work because your object contains other objects, you don't know how they work under the hood. That string, for instance, almost certainly can't be dumped the way you're trying to do it. Plus you aren't only dumping your data.
Also, you should printf the sizeof(k). You might find it interesting information. Try to account for every byte. You could printf the sizeof(k.name) to help you work some of it out.
I'm almost positive the information doing so would surprise you, but I haven't actually done it myself, because I would never try to raw memory copy C++ objects, and that's in effect what you're trying to do.
as the title suggests, I am having a problem with not being able to read from an input file after passing the ifstream object to a class function. Basically I'm trying to sort a list of numbers using a heap ADT implemented with an array.
int main() {
ifstream infile("input.txt");
HeapSort* heap = new HeapSort(20); // creates a heap (array) with size 20
heap->buildHeap(&infile);
return 0;
}
void HeapSort::buildHeap(ifstream* infile) {
int data;
while (infile >> data) {cout << data << endl;}
infile->close();
}
the error occurs in the conditional of the while loop inside buildHeap. The compiler can't recognize the operator ">>" between an 'int' and an 'ifstream' object. However, strangely enough, if I write that same while loop inside main(), it'll work just fine. Also of note is that if I remove the while loop, the compiler returns no errors. Meaning, simply the act of passing the ifstream object from main to buildHeap is OK.
Please avoid suggesting alternative ways of achieving this. I was asked to not use any special fstream functions like eof(). I can only use the ">>" operator to read from the desired file.
You're passing a pointer to a stream, so you need to dereference it:
while (*infile >> data)
If you want your code to look like what you say you did in main, then you pass a reference:
heap->buildHeap(infile);
//...
void HeapSort::buildHeap(ifstream& infile)
{
int data;
while (infile >> data) { ... }
infile.close();
}
I'm quite new to C++. I've been trying to figure this out for days - there'll be an easy solution no doubt but I haven't been able to find it (after much googling)! My problem is this:
I'm trying to create a class with a member function that reads in characters from a file and stores them in an array. I want to be able to create multiple objects (not sure how many - decided by the user), each with their own arrays filled with characters taken from different files. I think I've managed to do that. How would I then go about accessing the object's array in main?
The code I'm working on is long and messy but something along these lines (char.txt contains simply '12345' in this case):
#include <iostream>
#include <fstream>
using namespace std;
class Something{
public:
void fill_array(char array_to_fill[]){
char next;
ifstream input;
input.open("chars.txt");
input.get(next);
while(!input.eof())
{
for(int i = 0; i < 6; i++)
{
array_to_fill[i] = next;
input.get(next);
}
}
}
};
int main()
{
Something* something = new Something[1];
char array_to_fill[5];
something->fill_array(array_to_fill);
//I'd like to be able to access the array here; for example - to cout the array.
return 0;
}
Apologies if a) my terminology is wrong b) my code is rubbish or c) my question is stupid/doesn't make sense. Also I should add I haven't learnt vectors yet and I'm not supposed to use them for the program I'm making. Any help would be much appreciated. Cheers!
Your class does not store the array at all. It is simply a holder for a method. You probably want something like this, where each instance of the class holds the array. (I changed it to std::string since they are nicer to work with.)
class Something
{
private:
std::string data;
public:
void fill_data( const std::string& filename )
{
ifstream file( filename );
file >> data;
file.close();
}
std::string get_data() const
{
return data;
}
}
int main()
{
std::vector<Something> my_things;
my_things.push_back( Something() );
my_things[0].fill_data( "chars.txt" );
cout << my_things[0].get_data() << std::endl;
my_things.push_back( Something() );
my_things[1].fill_data( "another_file.txt" );
cout << my_things[1].get_data() << std::endl;
}
Since you are using C++, not C, get used to writing C++ code instead of C. (std::vector instead of C arrays (for unknown length arrays), std::string instead of char*, etc).
I think your question is too general for the format of stack overflow, but what you want in this case is to either create a public member, or create a private member with setters and getters.
class Something
{
public:
std::string m_string;
}
int main()
{
Something A;
A.m_string = "toto";
cout << A.m_string;
return 0;
}
Put a string for convenience (you could use a const char* but you will have to understand what is the scope to know when it will not be accessible anymore and you are not quite there yet) and there may be typos since I typed this from a phone.
If you really want to access the chars themselves, pass a char* with a size_t for the length of the array or use std::array if possible.
Right now the method fill_array is creating a local copy of array_to_fill, so any changes that you make to array_to_fill only happen in the local method. To change this, pass by pointer. This way the pointer gets copied instead of the whole array object. I didn't test this but it should look more like this:
void fill_array(char* array_to_fill){
...
}
You don't need to change anything in the main method.
To actually access the elements you can use [] notation. I.e. cout << array_to_fill[0] in the main method.
Edit: I think that change should work.
This program is written in C++. I am trying to use a void function to expand a Line structure which consists of an integer length and a pointer to the next connected line. There is a void Expand function made to assign a line reference to the line pointer in the struct. The new line is to be double the size of the current line. With the code I am using, I get a g++ error of 'Taking address of temporary [-fpermissive]'. Could anyone suggest a way in which the function adds a valid instance of a line reference to the Line pointer nextLine?
struct Line
{
int length;
Line* nextLine;
};
Line NewLine(Line& lineRef)
{
Line newLine;
newLine.length = lineRef.length * 2;
return newLine;
}
void Expand(Line& lineRef)
{
//Error here states: Taking address of temporary [-fpermissive]
lineRef.nextLine = &NewLine(lineRef);
}
int main() {
Line line;
Expand(line);
cout << line.length << endl;
cout << line.nextLine->length << endl;
return 0;
}
You're trying to implement a linked list, but you don't understand manual memory management yet.
The short-term solution is to use std::list<Line>. There's already a solution that works, and you don't need to bother with the behind-the-scenes stuff.
The long-term solution also is to use std::list<Line>. No need to re-invent the wheel, even if you're a seasoned developer and know how to.
The problem with the line:
lineRef.nextLine = &NewLine(lineRef);
is what the compiler is telling you. You are taking the address of a temporary. What it means is that after the ; is reached, the temporary NewLine(lineRef) will be destroyed and the pointer lineRef.nextLine will be pointer to a dead object.
Update: how to make it work.
It depends on what you want to do. If what you want is to have a list then the simplest thing is using a prepacked list data structure (std::list<Line>) rather than rolling your own implementation of list.
If you really want to implement your own list, then you will need to dynamically allocate the next node (this will make the compiler happy) and you will need to add code to manage the list (proper construction of the Line object that initializes the fields, including copy-construction, destructors to manage the dynamic memory, probably some helper functions to walk the list (or iterators to be able to use algorithms...) Just don't bother and use std::list.
This one works
struct Line
{
int length;
Line* nextLine;
~Line(){delete nextLine;}
//Make copy constructor and assignment operator private
};
void Expand(Line* lineRef)
{
lineRef->nextLine = new Line;
lineRef->nextLine->length = 2*(lineRef->length) ;
}
int main()
{
Line* line = new Line;
line->length = 5;
Expand(line);
cout << line->length << endl;
cout << line->nextLine->length << endl;
delete line;
return 0;
}