简体   繁体   中英

reading from .txt file and storing them to vector with class objects as values in c++

    vector<Person*> people;
ifstream in_stream;
string line;
string name;
int age;

in_stream.open("people.txt");
getline(in_stream, line);
stringstream sline(line);
sline >> name >> age;
Person a(name, age);
people.push_back(&a);


for (auto x : people)
    cout << *x << endl;

So i have this piece of code right here and im trying to read from a.txt file which is formed like that

Nick 23
Peter 27
Tom 42
Sofia 28
Daniel 39
Jonas 28

And save these people(name and age) in a vector which contains pointers to a class called Person. The code above works fine but when i put the getline in a while loop so i can get all lines of the txt is bugging out and the vector will be like that

28
28
28
28
28
28

No names just the age of the last person. Any ideas what im doing wrong here? Person.h

#pragma once
#include <ostream>
#include <istream>
#include <iostream>
using namespace std;

class Person {
protected:
    string name;
    size_t age;
    virtual void print(ostream& out)  const;
public:
    Person(const Person& p);
    Person(string n, size_t a);
    Person();
    friend bool operator==(const Person& lhs, const Person& rhs);
    friend ostream& operator<<(ostream& out, const Person& rhs);
    friend istream& operator>>(istream& in, Person& rhs);

};

Person.cpp

#include "Person.h"

Person::Person(const Person& p) : name{p.name}, age {p.age}
{
}

Person::Person(string n, size_t a) : name{ n }, age{ a }
{
}

Person::Person() : name{"default"}, age {0}
{
}

bool operator==(const Person& lhs, const Person& rhs)
{
    if (lhs.name == rhs.name && lhs.age == rhs.age) {
        return true;
    }
    else {
        return false;
    }

}

void Person::print(ostream& out) const
{
    out << name << " " << age;
}

ostream& operator<<(ostream& out, const Person& rhs)
{
    rhs.print(out);
    return out;
}

istream& operator>>(istream& in, Person& rhs)
{
    string x;
    int y;
    in >> x >> y;
    rhs.name = x;
    rhs.age = y;
    return in;
}

Any help is highly appreciated.

Let's say we have

struct Person {
  std::string name;
  unsigned age;
};

I imagine that the teacher wants you to allocate each object with new , store its pointer, and then manually delete it later, but that's just because the teacher is teaching the wrong thing. You're not supposed to write new code like that. Hold things by value (without any explicit pointers), or use smart pointers like std::unique_ptr .

What the teacher probably wants (or, mistaken, thinks he should be wanting):

Person *addPerson(std::vector<Person*> &people, std::string &&name, int age)
{ 
  auto *person = new Person(std::move(name), age);
  people.push_back(person);
  return person;
}

// optional
void removePerson(Person *person, std::vector<Person*> &people)
{
  auto where = std::find(people.begin(), people.end(), person);
  if (where != people.end())
  {
    delete *where; // * dereferences the iterator, giving the pointer back
    std::erase(where);
  }
}

void removeAllPeople(std::vector<Person*> &people)
{
  for (auto *person : people)
    delete person;
  people.clear();
}

int main()
{
  std::vector<Person*> people;

  //... add people, modify people, etc

  removeAllPeople(); // free the memory
}

What the teacher should be teaching instead:

// Hold people by value
std::vector<Person> people;

or maybe (even though it's unnecessary)

// Hold people using owning pointers
std::vector<std::unique_ptr<Person>> people;

What you could do instead is to generate the vector of pointers after the file has been read, ie

std::istream &operator>>(std::istream &is, Person &person)
{
  return is >> person.name >> person.age;
} 

std::ostream &operator<<(std::ostream &os, const Person &person)
{
  return os << person.name << ' ' << person.age << '\n';
}

std::vector<Person> readPeople(std::istream &from)
{
  std::vector<Person> people;
  Person person;

  while (from >> person) // while the read had succeeded
    people.push_back(std::move(person));

  return people;
}

std::vector<Person*> makePointersTo(std::vector<Person> &people)
{
  std::vector<Person*> pointers;
  pointers.reserve(people.size()); // since we know exactly how many items we need

  for (auto &person : people)
    pointers.push_back(&person);

  return pointers;
}

int main()
{
#if 1
  std::ifstream input;
  input.open("people.txt");
  if (!input.is_open()) abort();
#else
  // we could also be reading from standard input
  std::istream &input = std::cin;
#endif

  std::vector<Person> const peopleValues = readPeople(input);
  std::vector<const Person*> const people = makePointersTo(peopleValues);

  for (auto *person : people)
    std::cout << *person << '\n';

  // The memory will be freed by the vector destructors, automatically
}

It's unnecessary to overload the comparison operators etc. - you don't need to be comparing people, unless the assignment explicitly asks for it.

And also: the assignment is so small (a hundred lines or so) that it's just absurd to demand splitting it into multiple files. It's mind-bogglingly ridiculous. Separation of concerns is very important aspect of teaching. If you're introducing stream I/O, you shouldn't be mixing it with how to split a program into logical modules that expose interfaces ( .h files), but are implemented elsewhere ( .cpp files). And in fact, you shouldn't be encouraging anyone writing such small programs (<1000 lines) to be split into multiple files. It's counterproductive, always. It helps with nothing, just adds boilerplate, as if there was some boilerplate deity, whose priests' advice you had to follow or else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM