Read a Person object from file stream A simple concept yet I seem to have written it in a complicated way.

I have written some C++ source code that should:


read in a file
parse it
save the data to a class
print out all the saved class data


I am absolutely new to C++ and OOP and Programming and English.

#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;fstream&gt;
#include &lt;sstream&gt;
#include &lt;vector&gt;

class Person {
private:
    int age;
    std::string name;

public:
    void setAge(int age) {
        this-&gt;age = age;
    }

    int getAge() {
        return age;
    }

    void setName(std::string name) {
        this-&gt;name = name;
    }

    std::string getName() {
        return name;
    }

    void printInfo() {
        std::cout
            &lt;&lt; "Name: " &lt;&lt; getName() &lt;&lt; '\n'
            &lt;&lt; "Age: " &lt;&lt; getAge() &lt;&lt; '\n';
    }
};

std::istream&amp; operator&gt;&gt; (std::istream&amp; is, Person&amp; person) {
    int age;
    is &gt;&gt; age;

    std::string name;
    is &gt;&gt; name;

    person.setAge(age);
    person.setName(name);

    return is;
}

int main() {
    auto myfilename = "unknown-people.txt";

    std::ofstream myfile(myfilename, std::ios::binary | std::ios::trunc);
    myfile &lt;&lt; "3 Baby\n";
    myfile &lt;&lt; "48 Linus Torvalds\n";
    myfile &lt;&lt; "62 Bill Gates\n";
    myfile &lt;&lt; "115 Kane Tanaka\n";
    myfile.close();


    std::ifstream mysamefile(myfilename, std::ios::binary);

    std::string details;
    std::vector&lt;Person&gt; people;

    while (std::getline(mysamefile, details)) {
        Person person;
        std::istringstream personDetails(details);
        personDetails &gt;&gt; person;
        people.push_back(person);
    }

    mysamefile.close();


    for (std::vector&lt;Person&gt;::iterator it = people.begin(); it != people.end(); ++it) {
        it-&gt;printInfo();

        if (!(it != people.end() &amp;&amp; it == --people.end())) {
            std::cout &lt;&lt; "\n\n";
        }
    }
}
You seem to have a good grasp of the concepts - this is looking pretty good for a first effort!
Those accessors (setAge() + getAge(), and setName() + getName()) are exactly equivalent to making the members age() and name() public, which would be simpler and clearer.  I'll assume that you're later going to add some code to check that the values are reasonable (don't allow negative ages, for example, nor empty or whitespace-only names).
Note also that the &quot;get&quot; accessors can be marked const, because they don't change the Person:
int getAge() const {
    return age;
}

std::string getName() const {
    return name;
}

In passing, I'll note that storing age as a member might not be the best design, especially when storing to a file that might be read back several years later.  If we store the year of birth, that's something that won't normally change, and we can always calculate the age from it.

The operator&gt;&gt;() is mostly what I'd expect.  You can chain the reading like this:
int age;
std::string name;
is &gt;&gt; age &gt;&gt; name;

However, there's a bug here.  is &gt;&gt; name will read up to the next whitespace, but our names can have whitespace in them.  We actually want to read up to the next newline, like this:
std::string name;
std::getline(is, name);

Another consideration here is that we don't want to update the person if we failed to read both values (otherwise, operator&lt;&lt;() could be a friend function, and read directly to person.age and person.name).  We can easily test whether the reads succeeded:
if (is) {
    person.setAge(age);
    person.setName(name);
}

When we have a reader, it's a good idea to have a matching writer that produces the same format.  That's very simple to write:
std::ostream&amp; operator&lt;&lt;(std::ostream&amp; os, const Person&amp; person) {
    return os &lt;&lt; person.getAge() &lt;&lt; ' '
              &lt;&lt; person.getName() &lt;&lt; '\n';
}

Note that we have a reference to a const person here - the writer is promising not to modify person.

When reading the people in, we don't need to copy each line of text into a buffer (once we've fixed the bug I mentioned with reading names) - we can stream directly from the file stream into the person object:
std::vector&lt;Person&gt; people;

Person person;
while (mysamefile &gt;&gt; person) {
    people.push_back(std::move(person));
}

(The std::move() is a non-essential optimisation - it says we won't be using person until it's re-written using &gt;&gt;).

When displaying the people, we can make the code simpler using range-based for like this:
for (const Person&amp; person: people) {
    person.printInfo();
    std::cout &lt;&lt; &quot;\n\n&quot;;
}

Of course, this is slightly different, because we're now printing the newlines after the last person too.  A good way to solve this is to print the newlines before each person, but not if it's the first person.  To do this, we can initially set our separator to be an empty string, and update after it's used:
auto separator = &quot;&quot;;
for (const Person&amp; person: people) {
    std::cout &lt;&lt; separator;
    separator = &quot;\n\n&quot;;

    person.printInfo();
}

A nice touch would be to allow printing to somewhere other than std::cout, by passing the stream as an argument to printInfo():
void printInfo(std::ostream&amp; out = std::cout) const {
    out &lt;&lt; &quot;Name: &quot; &lt;&lt; name &lt;&lt; '\n'
        &lt;&lt; &quot;Age: &quot; &lt;&lt; age &lt;&lt; '\n';
}


Modified code
I've rearranged the headers a bit, to ease the process for when you want to separate the class definition from the test program.  I've changed the file read/write to work purely in memory for the test (I don't like tests that leave files lying about; using real files also increases fragility of the tests).  And I've added some validation of ages and names.
#include &lt;algorithm&gt;
#include &lt;istream&gt;
#include &lt;ostream&gt;
#include &lt;stdexcept&gt;
#include &lt;string&gt;

class Person
{
private:
    int age;
    std::string name;

public:
    void setAge(int age) {
        if (age &lt; 0) {
            throw std::range_error(&quot;Negative age&quot;);
        }
        this-&gt;age = age;
    }

    int getAge() const {
        return age;
    }

    void setName(std::string name) {
        auto first_nonspace
            = std::find_if_not(name.begin(), name.end(),
                               [](unsigned char c){ return std::isspace(c); });
        if (first_nonspace == name.end()) {
            throw std::range_error(&quot;Invalid name&quot;);
        }
        this-&gt;name = name;
    }

    std::string getName() const {
        return name;
    }

    void printInfo(std::ostream&amp; out) const {
        out &lt;&lt; &quot;Name: &quot; &lt;&lt; name &lt;&lt; '\n'
            &lt;&lt; &quot;Age: &quot; &lt;&lt; age &lt;&lt; '\n';
    }
};

std::istream&amp; operator&gt;&gt;(std::istream&amp; is, Person&amp; person)
{
    int age;
    is &gt;&gt; age;

    std::string name;
    std::getline(is, name);

    if (is) {
        person.setAge(age);
        person.setName(name);
    }

    return is;
}

std::ostream&amp; operator&lt;&lt;(std::ostream&amp; os, const Person&amp; person)
{
    return os &lt;&lt; person.getAge() &lt;&lt; ' '
              &lt;&lt; person.getName() &lt;&lt; '\n';
}


#include &lt;iostream&gt;
#include &lt;sstream&gt;
#include &lt;vector&gt;

int main()
{
    std::ostringstream myfile;
    myfile &lt;&lt; &quot;3 Baby\n&quot;;
    myfile &lt;&lt; &quot;48 Linus Torvalds\n&quot;;
    myfile &lt;&lt; &quot;62 Bill Gates\n&quot;;
    myfile &lt;&lt; &quot;115 Kane Tanaka\n&quot;;

    std::istringstream mysamefile(myfile.str());

    std::vector&lt;Person&gt; people;
    Person person;
    while (mysamefile &gt;&gt; person) {
        people.push_back(std::move(person));
    }

    auto separator = &quot;&quot;;
    for (const Person&amp; person: people) {
        std::cout &lt;&lt; separator;
        separator = &quot;\n\n&quot;;

        person.printInfo(std::cout);
    }
}