-2

I'm guessing I might have to use pointers, but haven't gone in depth too much on them in class yet to try and implement them in my program. I have this so far, the printing function is towards the middle of the program. I'm not quite sure on how to print out the elements from the vector as my approach didn't work.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class rolodex
{
  string name;
  string street, town, state;
  string zip;
  string phone;
  vector <rolodex> entries;

  public:
     rolodex();
     void getmenu();
     void add_entry();
     void set_name();
     void set_address();
     void set_phone();
     void printinfo();
};

rolodex :: rolodex() : name(""), street(""), town(""), state(""), zip(""), 
phone(""), entries()
{
}


void rolodex :: getmenu()
{
  cout << "\n\n1)Add Entry";
  cout << "\n5)Print All Entries";
  cout << "\n6)Exit" << endl;
}


void rolodex :: add_entry()
{
  rolodex temp;
  cout << "\n\nEnter Name: ";
  temp.set_name();
  temp.set_address();
  cout << "\n\nEnter Your Phone Number: ";
  temp.set_phone();

  entries.push_back(temp);

}

void rolodex :: set_name()
{
  cin.ignore();
  getline(cin, name);

}


void rolodex :: set_address()
{
  cout << "\n\nNow we'll enter address information.";
  cout << "\n\nStreet: ";
  getline(cin, street);
  cout << "\n\nTown: ";
  getline(cin, town);
  cout << "\n\nState: ";
  getline(cin, state);
  cout << "\n\nZip: ";
  getline(cin, zip);
}


void rolodex :: set_phone()
{
  getline(cin, phone);
}


void rolodex :: printinfo()
{

  for(unsigned int i = 0; i < entries.size(); i++)
  {
     cout << entries[i] << endl;  //This is where I'm stuck since I've only
                                  //worked with vectors of non-object data
                                  //type
  }

}


int main()
{
  rolodex person, menu;
  short choice;
  bool done = false;


 do
 {
   menu.getmenu();
   cout << "\n\nEnter a choice: ";
   cin >> choice;

   switch(choice)
   {
      case 1:
         person.add_entry();
         break;
      case 5:
         person.printinfo();
         break;
      case 6:
         done = true;
         break;

      default:
         cout << "\n\nInvalid Entry." << endl << endl;
     }

   } while(!done && isdigit(choice));

   return 0;
}
  • 3
    Provide an overloaded `std::ostream& operator<<(std::ostream, const rolodex&)` – πάντα ῥεῖ Oct 06 '18 at 08:48
  • 4
    A small note about your design (but unrelated to your problem): A rolodex is a *collection of **persons***. A rolodex isn't a combination of a single person *and* a collection of persons. And keep things like "user interface" separate as well, it's not really part of the actual rolodex either. – Some programmer dude Oct 06 '18 at 08:49

2 Answers2

1

πάντα ῥεῖ is right, but to add a little more detail...

You need to specify how you want the stream to handle your object. This is done by by adding a << operator. For example:

std::ostream& operator<<(std::ostream& s, const rolodex& r){
  // Or however you want to format it.
  s << "Name: " << r.name << " : ";
  s << "Street: " << r.street << " : ";
  s << "Town: " << r.town << " : ";
  s << "State: " << r.state << " : ";
  s << "Zip: " << r.zip << "\n";
}

Unfortunately, the function above tries to access the private fields of your class, which it can't because it is not part of the class definition.

An easy way to address that is to declare this function a "friend" inside of the class definition, like such:

 friend std::ostream& operator<<(std::ostream&, const rolodex&);

...And since you might appreciate it, one big copy-pasteable chunk that you can use directly that should make your function work:

class rolodex
{
  string name;
  string street, town, state;
  string zip;
  string phone;
  vector <rolodex> entries;

  public:
     rolodex();
     void getmenu();
     void add_entry();
     void set_name();
     void set_address();
     void set_phone();
     void printinfo();

     friend std::ostream& operator<<(std::ostream&, const rolodex&);
};

std::ostream& operator<<(std::ostream& s, const rolodex& r){
  // Or however you want to format it.
  s << "Name: " << r.name << " : ";
  s << "Street: " << r.street << " : ";
  s << "Town: " << r.town << " : ";
  s << "State: " << r.state << " : ";
  s << "Zip: " << r.zip << "\n";
}
  • Would I do the actual printing in int main()? Your feedback has helped a lot, but I'm still having trouble outputting anything. It just prints the "Name: " and " : " any further feedback would be greatly appreciated – Johnny Vazquez Oct 06 '18 at 20:04
  • If the "Name: " and " : " are showing up, but don't have anything in between them, that means the fields in the object are empty. I just tried compiling the program, and the program exited after I tried setting a person's information. I don't think the isdigit function is really what you are looking for to keep you in that loop. Edit: In fact, just removing that `&& isdigit(choice)` from the while condition made it behave like I think you intended. – Shawn Dooley Oct 07 '18 at 14:46
  • Yeah the isdigit was working differently at first and was not exiting the loop, but I took it out when I realized it was exiting after inputting info. Ideally I wanted to make a temporary object and retrieve name, address, and phone number inside the class and then input them into a vector so i could have a vector of objects. Problem is I don't know how to properly access individual elements inside those objects inside of that class. I opted for 3 parallel string vectors instead, which I think is less efficient. Thanks for the help, I'm gonna try both versions either way – Johnny Vazquez Oct 07 '18 at 18:07
1

Following up on πάντα ῥεῖ's suggestion, here's one way of doing that, changing your design as little as possible:

1) Create a non-member overloaded operator<< for your rolodex class:

std::ostream& operator<< (std::ostream& os, const rolodex& rol)
{
    os << rol.name << ":"    << std::endl
       << "\t" << rol.street << std::endl
       << "\t" << rol.town   << std::endl
       << "\t" << rol.state  << std::endl
       << "\t" << rol.zip    << std::endl
       << "\t" << rol.phone  << std::endl;
    return os;
}

.. but the compiler will chide you for attempting to access private members (by default, members are private) from outside the class, so you would have to relax the rules a bit:

class rolodex
{
  ...
public:
     ...
     friend std::ostream& operator<< (std::ostream& os, const rolodex& rol);
};

You can't have the operator<< inside the class itself, see does-overloading-operator-works-inside-the-class.

However, it is almost always better design to add getter functions to your public interface anyway. You would have get_name() etc in the public: section of your class def, those functions would initially just return the values of the private member variables, and then your operator<< can use them instead of trying to access the private members. You then no longer require the friend declaration.

I upvoted Some programmer dude's remark about your design

The code for letting the use input the data really shouldn't be inside the rolodex class, because it makes the class hard to reuse. Image wanting to re-use the rolodex from a graphical interface, for example, and it's not such a good idea to have the rolodex contain instances of itself inside the vector.

I would suggest a

1) Person class containing all the person's attributes, with public getters get_name() and setters set_name() that don't use a specific entry method, just take the data as arguments e.g. set_name(std::string& name).

2) an non-member operator<< to output a person to an output stream

3) a Rolodex class with a private std::vector<Person> and methods to add a person, write all the persons to an output stream, etc..

Good luck & enjoy :-)

Edit: the menu structure on the terminal should IMHO be left inside the main() function or encapsulated into another class. But certainly don't leave it in Rolodex or worse, Person.