0

My problem as follows:

I want to read values from a file into a dynamic array of specific class called Voter, using an overloaded >> operator. Then, i want to export my result to the screen to check my load with another overloaded << operator.

Using a normal array I get the result im after, but struggling with going dynamic. I have read on the forums that vectors are maybe a better way to go, but I want to understand what I'm doing wrong on this one.

Thanks!

Details:

The file has 3 "values" seperated by a space: id(string) nr(int) (1 or 0)bool

Code extract:

string RollFileName = "VotersRoll.dat";
ifstream inFile_VotersRoll;
inFile_VotersRoll.open(RollFileName);

if(inFile_VotersRoll.fail())
{
    printf("The VotersRoll.dat file failed to open.\n");
    exit(1);

}
//connect to in file



int numberOfEntries = 0;
Voter testVoter;
while(inFile_VotersRoll>>testVoter)
{
    numberOfEntries++;
}
//get number of voter objects from file

typedef Voter* VoterArrayPntr;
VoterArrayPntr rollArray;
rollArray = new Voter[numberOfEntries];
//create dynamic arrray for voter objects


while (inFile_VotersRoll>>*rollArray) {
    cout << *rollArray;

}

Class (extract):

class Voter
{
public:
    friend istream& operator>>(istream &inP, Voter &voterP);
    friend ostream& operator<<(ostream &outP,const Voter &voterP);
private:
    string id;
    int nr_times_voted;
    bool voted;
}

Friend functions:

istream& operator>>(istream &inP,Voter &voterP)
{  
   inP >> voterP.id >> voterP.nr_times_voted >> voterP.voted;
   return inP; 
}
ostream& operator<<(ostream &outP,const Voter &voterP)
{ 
   outP << voterP.id << voterP.nr_times_voted << voterP.voted;   
   return outP;
}
6
  • What are your input, output & error messages? Please read this. Commented Feb 21, 2015 at 9:52
  • Thanks philipxy - not really receiving any error messages: I'm reading in this (the VotersRoll.dat file data): 19810102009 1 0 19792003008 2 0 19851010890 3 1 19900909897 2 0 19561812567 6 0 19682703345 7 1 With output currently just: Program ended with exit code: 0 Commented Feb 21, 2015 at 9:55
  • 1
    @Gpoo Please edit your question to put such additional information. As you can see it's not very well readable in a comment. Commented Feb 21, 2015 at 9:56
  • 1
    Do you "rewind" the file after you read it the first time? Commented Feb 21, 2015 at 9:59
  • @JoachimPileborg Thanks Joachim. No I do not. This looks to be the problem. Any guidance on how one would accomplish this? Just starting out with c++ so still learning the ins and outs. Commented Feb 21, 2015 at 10:16

2 Answers 2

1

In C++, std::vector implements the concept of a dynamic array. This is not just "maybe" a better way. Don't use new[] and delete[]!

(In fact, delete[] is entirely missing from your example code. If you don't release the memory anywhere and the object is not conceptually meant to exist as long as the program runs, then that's a memory leak.)

As for why the code doesn't work here, let's first have a look at this loop:

int numberOfEntries = 0;
Voter testVoter;
while(inFile_VotersRoll>>testVoter)
{
    numberOfEntries++;
}

This is curious; your first read the whole file just to get the number of elements, and will then read it again to get the actual elements?

This is redundant. You should get everything in one loop.

But there are also two serious bugs here:

  1. You have arrived at the end of the file before the second loop starts.
  2. You use only the first element of your array.

Let's fix the first problem first. In the next loop,

typedef Voter* VoterArrayPntr;
VoterArrayPntr rollArray;
rollArray = new Voter[numberOfEntries];
//create dynamic arrray for voter objects

while (inFile_VotersRoll>>*rollArray) {
    cout << *rollArray;
}

the loop condition will never be true. Your operator>> is called once. The stream is returned from the operator. And finally, it is used in the loop condition, resulting in false – because you had already been at the end of the file.

A very quick fix would be to open a new stream for the same file to fix this:

typedef Voter* VoterArrayPntr;
VoterArrayPntr rollArray;
rollArray = new Voter[numberOfEntries];
//create dynamic arrray for voter objects

ifstream inFile_VotersRoll2; // ugly workaround
inFile_VotersRoll2.open(RollFileName);
while (inFile_VotersRoll2>>*rollArray) {
    cout << *rollArray;
}

This will read and print the whole file. But still, everything will have been read into and written from only the first element of the array. *rollArray refers to the first element, and rollArray never changes.

This second bug is fixed like this:

for (int index = 0; (index < numberOfEntries) && (inFile_VotersRoll2>>rollArray[index]); ++index) { 
    cout << rollArray[index];
}

But as I said before, your goal should be to read the whole thing at once. This implies increasing the array's size as you go. Fortunately, with std::vector, this is trivial, because std::vector does not only grow automatically, it also does so in a clever way. And the memory is released automatically as well. Here we go, your complete example modified to use std::vector:

#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <stddef.h>

using namespace std; // just for quick'n'dirty test code

class Voter
{
public:
    friend istream& operator>>(istream &inP, Voter &voterP);
    friend ostream& operator<<(ostream &outP,const Voter &voterP);
private:
    string id;
    int nr_times_voted;
    bool voted;
};

istream& operator>>(istream &inP,Voter &voterP)
{  
   inP >> voterP.id >> voterP.nr_times_voted >> voterP.voted;
   return inP; 
}
ostream& operator<<(ostream &outP,const Voter &voterP)
{ 
   outP << voterP.id << voterP.nr_times_voted << voterP.voted;   
   return outP;
}

int main() {

    string RollFileName = "VotersRoll.dat";
    ifstream inFile_VotersRoll;
    inFile_VotersRoll.open(RollFileName);

    if(inFile_VotersRoll.fail())
    {
        std::cerr << "The VotersRoll.dat file failed to open.\n";
        return EXIT_FAILURE;
    }

    std::vector<Voter> voters;

    Voter input;
    while (inFile_VotersRoll >> input) {
        voters.push_back(input);
    }

    for (auto const &voter : voters) {
        std::cout << voter << "\n";
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Christian. This has really helped me a lot. Still learning, so this type of post helps quite a lot! Another question: in my source file I basically have a matrix (first column: string, second column: integer, third column: bool) How would one read column 2 and 3 in using vectors as well?
@Gpoo: You would either have a std::vector<std::vector<T>> or a std::vector<T> of size width*height, with additional logic to calculate the right offset. In both cases, you would wrap the vector in a Matrix class to encapsulate the additional complexity. See stackoverflow.com/questions/27656251/…
0

"I want to understand what I'm doing wrong on this one."

Your loop here

while (inFile_VotersRoll>>*rollArray) {
    cout << *rollArray;
}

will only write to the 1st element in the array. You need to increment the pointer to the next element after reading in:

inFile_VotersRoll.seekp(0); // Rewind to the beginning of inFile_VotersRoll

VoterArrayPntr curItem = rollArray;
while (inFile_VotersRoll>>*curItem) {
    cout << *curItem;
    curItem++; // <<<<<<
}

4 Comments

The problems with this is of course that the original pointer is lost.
Thanks πάντα ῥεῖ, I tried it, but still not displaying anything. Maybe any other suggestions I can try out?
@Gpoo " but still not displaying anything", well that must be a different problem.
@Gpoo You must reset the read pointer of inFile_VotersRoll, after you counted the number of entries: inFile_VotersRoll.seekp(0);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.