Simple address book in C++ - follow-up
Misuse of inheritance

The huge, glaring problem I see with this code can be shown in one line:

class Address_book : public Person


This looks to me like a completely broken architecture. As a rule, public inheritance should follow the Liskov Substitution Principle (LSP), and this isn't even close1.

Before Barbara Liskov formulated the substitution principle, the usual phrasing was that public inheritance should reflect an "Is-a" relationship. The LSP attempts to...tighten that a little to deal with a few odd cases like circles vs. ellipses, where you can say a circle is a specific case of an ellipse, but (since you can't always substitute a circle where an ellipse is required) you shouldn't normally use public inheritance.

In this case, however, we don't need the more careful formulation. An address book isn't a person, and isn't really even anything like a person. We don't need to look much further to see that this is use (abuse?) of inheritance is problematic at best.

Consistent interface

I'd also change the interface a little bit. Right now, you have read and write member functions for Person, and operator&lt;&lt; and operator&gt;&gt; for an address book.

I'd prefer to change this so the public interface is more consistent. Since the (more or less) standard way to do formatted I/O is to use operator&lt;&lt; and operator&gt;&gt;, I'd use those for both classes.

Resizing

Right now, to resize your vector, you have code like:

//sets vector size, creates person elements of vector
void Address_book::num_of_ent(int entries, Address_book&amp; abook) {
    for (int i = 0; i &lt; entries; ++i) {
            Person p;
        abook.add_book.push_back(p);
    }
}


I'm not fond of the name (at first blush, the name sounds more like it should be retrieving the current number of entries). I'd also prefer to use vector's resize() member instead. Finally, a member function that's going to modify an object should normally modify the object of which it's a member, not have some other object passed to it to modify.

void Address_book::resize(int entries) {
    add_book.resize(entries);
}


Size type

That brings us to the next point: the type for the size of a container is preferably container_type::size_type or if that's not easily available, you can usually use size_t as a fair approximation. So that means the resize should really be changed to:

void Address_book::resize(size_type entries) {
    add_book.resize(entries);
}


...where size_type is something like:

typedef vector&lt;Person&gt;::size_type size_type;


...or:

using size_type = vector&lt;Person&gt;::size_type;


formatting of output

A fairly minor point is that by convention writing an object to the stream should only write the content of that object to the stream, not write a delimiter that separates that object from the next. For example, here:

//operator for address book person element
std::ostream&amp; Person::print(std::ostream&amp; os, const Person &amp;p1) {
    os &lt;&lt; "Name: " &lt;&lt; p1.name &lt;&lt; "\nAddress: " &lt;&lt; p1.address &lt;&lt; "\n\n";
    return os;
}


...we really want to get rid of the trailing new-lines, and leave that to the client code.

std::ostream&amp; Person::print(std::ostream&amp; os, const Person &amp;p1) {
    os &lt;&lt; "Name: " &lt;&lt; p1.name &lt;&lt; "\nAddress: " &lt;&lt; p1.address;
    return os;
}


Then we might use it something like this:

std::copy(my_book.begin(), my_book.end(), 
          std::ostream_iterator&lt;Person&gt;(std::cout, delimiter));


...and to get the same format, we'd have delimiter = "\n\n";, but if we wanted the output single-spaced, it'd be "\n", and so on.




1. Nor do I see any indication that this might be an exception to the rule.